Компилятор вынужден отклонить недействительный constexpr?

#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 недействителен).

9

Решение

Насколько я понимаю, да, каждый компилятор должен жаловаться на утверждение (3).

N3242 7.1.5 пункт 9:

constexpr спецификатор, используемый в объявлении объекта, объявляет объект как const, Такой объект должен иметь буквальный тип и должен быть инициализирован. Если он инициализирован вызовом конструктора, этот вызов должен быть константным выражением (5.19). В противном случае каждое полное выражение, которое появляется в его инициализаторе, должно быть константным выражением. Каждое неявное преобразование, используемое при преобразовании выражений инициализатора, и каждый вызов конструктора, используемый для инициализации, должен быть одним из тех, которые разрешены в константном выражении (5.19).

Я думаю о constexpr объект «оценивается во время компиляции», и constexpr функция или constexpr конструктор как «может быть оценен во время компиляции». Компилятор должен определить семантическую достоверность операторов типа (3) во время компиляции. Вы можете утверждать, что «оценка» все еще может быть выполнена во время выполнения, но проверка на достоверность делает большую часть этой работы в любом случае. Кроме того, код может продолжить создание экземпляра шаблона, например Check<y>, что в значительной степени гарантирует компилятору вычислить значение y во время компиляции.

Это означает, что вы могли бы написать дьявольскую программу, чтобы компилятор занимал очень много времени или очень долго. Но я подозреваю, что это было уже возможно с operator-> трюки.

8

Другие решения

Я уверен, что (1) должно привести к ошибке компиляции. Я совершенно уверен, что (2) не должен быть отклонен во время компиляции, хотя он потерпит неудачу во время выполнения.

Правильный. throw часть условного оператора не является константным выражением, и в (1) она не является недооцененной. Для (2), foo не должен быть оценен во время компиляции.

Для (3), как компилятору будет разрешено отложить оценку? constexpr Децл спецификатор сил foo быть оцененным во время компиляции. Это в основном так же, как (1), инициализация y это контекст, где константное выражение требуется.

§7.1.6 [dcl.constexpr] p9

constexpr спецификатор, используемый в объявлении объекта, объявляет объект как const, Такой объект должен иметь буквальный тип и должен быть инициализирован. Если он инициализируется вызовом конструктора, этот вызов должен быть константным выражением (5.19). Иначе, или если constexpr спецификатор используется в ссылочной декларации, каждое полное выражение, которое появляется в его инициализаторе, должно быть константным выражением. […]

5

По вопросам рекламы [email protected]