поведение std :: is_constant_evaluated

GCC9 уже реализует std::is_constant_evaluated. Я немного поиграл с этим, и понял, что это несколько сложно. Вот мой тест:

constexpr int Fn1()
{
  if constexpr (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

constexpr int Fn2()
{
  if (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

int main()
{
  constexpr int test1 = Fn1(); // Evaluates to 0
  int test2 = Fn1();           // Evaluates to 0
  int const test3 = Fn1();     // Evaluates to 0

  constexpr int test4 = Fn2(); // Evaluates to 0
  int test5 = Fn2();           // Evaluates to 1
  int const test6 = Fn2();     // Evaluates to 0
}

По этим результатам я извлек следующие выводы:

  • if constexpr (std::is_constant_evaluated()) всегда оценивает true ветвь. Поэтому нет смысла использовать эту конструкцию.

  • Если компилятор оценивает переменную во время компиляции, std::is_constant_evaluated()) имеет значение true, независимо от того, является ли эта переменная явно аннотированной constexpr или нет.

Я прав?

Ответ 1

if constexpr требует постоянного выражения для условия. Поэтому is_constant_evaluated конечно, всегда будет верным в таком контексте.

Это означало для обычного, if. Цель состоит в том, чтобы не входить в путь кода, который является недопустимым в функции constexpr при оценке в константном выражении. Но чтобы он выполнялся во время выполнения. Это не для того, чтобы полностью исключить эти пути кода из функции.

Ответ 2

Вот как я думаю об этом, может быть, вы найдете это полезным... может быть, нет. Обратите внимание, что я думаю, что написание if constexpr (std::is_constant_evaluated()) будет действительно распространенной ошибкой, и в нее легко попасть. Но, надеюсь, компиляторы просто диагностируют этот случай. Отправлено 91428, исправлено для gcc 10.1.


По сути, у нас есть два разных правила для кода - типичные правила для нормального кода времени выполнения и ограничения для константных выражений, которые предназначены для программирования constexpr. Это ограничения expr.const: нет UB, нет reinterpret_cast и т.д. Эти ограничения продолжают уменьшаться от языкового стандарта к языковому стандарту, и это здорово.

По сути, поток управления (с точки зрения пути кода) чередуется между режимом "полного времени выполнения" и режимом constexpr. Как только мы войдем в режим constexpr (будь то инициализация объекта constexpr или оценка параметра шаблона или...), мы останемся там до тех пор, пока не закончим... и затем вернемся к полному режиму выполнения.

is_constant_evaluated() делает просто: я в режиме constexpr? Он сообщает вам, если вы находитесь в контексте, который требует константных выражений.

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

Однако для if (is_constant_evaluated()) простой if не меняет наше состояние между временем выполнения и constexpr. Таким образом, значение здесь зависит от контекста, из которого он был вызван. Инициализация test4 переводит нас в режим constexpr, потому что это объект constexpr. Во время его инициализации мы следуем правилам константных выражений... поэтому is_constant_evaluated() верно. Но как только мы закончим, мы вернемся к правилам времени выполнения... поэтому при инициализации test5 is_constant_evaluated() имеет значение false. (И затем test6 - это особый случай, к сожалению, язык - вы можете использовать постоянные целочисленные переменные в качестве константных выражений, поэтому мы используем их инициализацию одинаково для этих целей.)