#include <exception>
constexpr bool foo(bool x)
{
return x ? true : throw std::exception();
}
int main()
{
// 1) must never be compiled
// static_assert(foo(false), "");
// 2) must always be compiled?
const bool x = foo(false);
// 3) must never compile?
constexpr bool y = foo(false);
return 0;
}
Я уверен, что (1) должно привести к ошибке компиляции. Я совершенно уверен, что (2) не должен быть отклонен во время компиляции, хотя он потерпит неудачу во время выполнения.
Интересным случаем является переменная constexpr (3). В этом простом примере gcc и clang фактически вычисляют выражение и поэтому отклоняют программу. (Сообщение об ошибке: y не является константным выражением).
Каждый компилятор C ++ 11 вынужден отклонять программу? Что если foo (false) было заменено более сложным выражением?
Я был удивлен, обнаружив, что constexpr не был завершен по Тьюрингу, хотя это произойдет после изменения спецификации:
Завершены ли вычисления Тьюринга на основе constexpr?
Может быть, это связано с моим вопросом. Насколько я понимаю, компилятору разрешено откладывать фактическую оценку constexpr (3) в этом примере до времени выполнения. Но если constexpr завершен по turing, мне трудно поверить, что компилятор может решить для всех constexpr, будет ли выброшено исключение (что означает, что constexpr недействителен).
Насколько я понимаю, да, каждый компилятор должен жаловаться на утверждение (3).
N3242 7.1.5 пункт 9:
constexpr
спецификатор, используемый в объявлении объекта, объявляет объект какconst
, Такой объект должен иметь буквальный тип и должен быть инициализирован. Если он инициализирован вызовом конструктора, этот вызов должен быть константным выражением (5.19). В противном случае каждое полное выражение, которое появляется в его инициализаторе, должно быть константным выражением. Каждое неявное преобразование, используемое при преобразовании выражений инициализатора, и каждый вызов конструктора, используемый для инициализации, должен быть одним из тех, которые разрешены в константном выражении (5.19).
Я думаю о constexpr
объект «оценивается во время компиляции», и constexpr
функция или constexpr
конструктор как «может быть оценен во время компиляции». Компилятор должен определить семантическую достоверность операторов типа (3) во время компиляции. Вы можете утверждать, что «оценка» все еще может быть выполнена во время выполнения, но проверка на достоверность делает большую часть этой работы в любом случае. Кроме того, код может продолжить создание экземпляра шаблона, например Check<y>
, что в значительной степени гарантирует компилятору вычислить значение y
во время компиляции.
Это означает, что вы могли бы написать дьявольскую программу, чтобы компилятор занимал очень много времени или очень долго. Но я подозреваю, что это было уже возможно с operator->
трюки.
Я уверен, что (1) должно привести к ошибке компиляции. Я совершенно уверен, что (2) не должен быть отклонен во время компиляции, хотя он потерпит неудачу во время выполнения.
Правильный. throw
часть условного оператора не является константным выражением, и в (1) она не является недооцененной. Для (2), foo
не должен быть оценен во время компиляции.
Для (3), как компилятору будет разрешено отложить оценку? constexpr
Децл спецификатор сил foo
быть оцененным во время компиляции. Это в основном так же, как (1), инициализация y
это контекст, где константное выражение требуется.
§7.1.6 [dcl.constexpr] p9
constexpr
спецификатор, используемый в объявлении объекта, объявляет объект какconst
, Такой объект должен иметь буквальный тип и должен быть инициализирован. Если он инициализируется вызовом конструктора, этот вызов должен быть константным выражением (5.19). Иначе, или еслиconstexpr
спецификатор используется в ссылочной декларации, каждое полное выражение, которое появляется в его инициализаторе, должно быть константным выражением. […]