поведение 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
- это особый случай, к сожалению, язык - вы можете использовать постоянные целочисленные переменные в качестве константных выражений, поэтому мы используем их инициализацию одинаково для этих целей.)