Постоянные целые и постоянная оценка

Рассмотрим следующую программу:

#include <iostream>
#include <type_traits>

constexpr int f() {
  if (std::is_constant_evaluated())
    return -1;
  else return 1;
}

int main() {
  int const i = f();
  std::cout << i;
}

It печатает -1 при запуске (wandbox).

Однако, если я сделаю функцию throw при оценке во время компиляции ::

#include <iostream>
#include <type_traits>

constexpr int f() {
  if (std::is_constant_evaluated())
    throw -1; // <----------------------- Changed line
  else return 1;
}

int main() {
  int const i = f();
  std::cout << i;
}

он хорошо компилируется и выдает 1 (wandbox). Почему я не получил ошибку компиляции?

Ответы

Ответ 1

Разве постоянная оценка - это не весело?

В языке есть несколько мест, где мы пытаемся выполнять постоянную оценку, и если это не удается, мы возвращаемся к выполнению постоянной оценки non-. Статическая инициализация - одно из таких мест, инициализация константных целых чисел - другое.

Что происходит с:

int const i = f();

это то, что это может быть постоянная оценка, но это не обязательно должно быть. Поскольку (non- constexpr) постоянные целые числа все еще можно использовать в качестве константных выражений, если они удовлетворяют всем другим условиям, мы должны попытаться. Например:

const int n = 42;       // const, not constexpr
std::array<int, n> arr; // n is a constant expression, this is ok

Итак, попробуем сделать - мы называем f() постоянным выражением. В этом контексте std::is_constant_evaluated() - это true, поэтому мы попадаем на ветку с помощью throw и в итоге терпим неудачу. Невозможно throw во время постоянной оценки, поэтому наша постоянная оценка не удалась.

Но затем мы отступаем и пытаемся снова - на этот раз мы называем f() константным выражением non- (то есть std::is_constant_evaluated() - это false). Этот путь завершается успешно, давая нам 1, поэтому i инициализируется значением 1. Но примечательно, что i не является постоянным выражением в этой точке. Последующий static_assert(i == 1) будет неправильно сформирован, потому что инициализатор i не является константным выражением! Даже если путь инициализации константы non- (в противном случае) полностью удовлетворяет требованиям константного выражения.


Обратите внимание: если мы попробуем:

constexpr int i = f();

Это не удалось бы, потому что мы не можем вернуться к константе инициализации non-.