Рассмотрим следующий код:
#include <type_traits>
int foo(int arg) {
if (std::is_constant_evaluated()) {
return 1;
} else {
return 0;
}
}
int main() {
const auto b = foo(0);
return b;
}
Он возвращает 0 как с gcc, так и с clang. Я бы ожидал, что вместо этого он вернет 1.
Если foo()
создается constexpr
, а b
сохраняется просто const
, то возвращается 1.
Что мне здесь не хватает? Спасибо!
const
не означает, что значение должно быть известно во время компиляции, если это недоразумение.
Все вызовы, отличные от constexpr, происходят во время выполнения. Во время компиляции запускаются только функции constexpr
или consteval
. Вот почему они были введены.
и почему вы ожидаете, что он вернет 1? это l-значение, которое не оценивается
Поскольку сгенерированный ассемблерный код показывает, что функция была оптимизирована, следовательно, "вызывается" во время компиляции. После прочтения предложения P0595R2 существует различие между «временем компиляции» и «постоянным оцениваемым контекстом». Я считаю, что мой пример похож на «звонок (3)», описанный в разделе «Введение и мотивация».
Вы должны быть немного осторожны с тем, где и как вы используете is_constant_evaluated
. В C++ есть 3 вида функций, и is_constant_evaluated
имеет смысл только в одном из них.
// a strictly run-time function
int foo(int arg)
{
if (std::is_constant_evaluated()) // pointless: always false
// ...
}
// a strictly compile time function
consteval int foo(int arg)
{
if (std::is_constant_evaluated()) // pointless: always true
// ...
}
// both run-time and compile-time
constexpr int foo(int arg)
{
if (std::is_constant_evaluated()) // ok: depends on context in
// which `foo` is evaluated
// ...
}
Еще одна распространенная ошибка, на которую стоит обратить внимание, заключается в том, что is_constant_evaluated
также не имеет смысла в условии if constexpr
:
{
if constexpr (std::is_constant_evaluated()) // pointless: always true
// regardless of whether foo
// is run-time or compile-time
}
std::is_constant_evaluated()
возвращает true тогда и только тогда, когда [meta.const.eval]:
оценка вызова происходит в рамках оценки выражения или преобразования, которое явно оценивается как константа.
Этот термин «явно оцениваемый с константой» (определенный здесь) относится к контекстам, которые должны оцениваться с константой. Вызов функции, отличной от constexpr
(ближайший объемлющий контекст здесь), никогда не оценивается как константа, потому что он не является constexpr
, так что это явно не «явно вычисляемая константа».
Однако, как только мы это делаем constexpr
, мы попадаем в эту странную наследственную причуду. До C++11, который представил constexpr
, мы все еще могли делать такие вещи:
template <int I> void f();
const int i = 42; // const, not constexpr
f<i>(); // ok
По сути, у нас есть это специально для целочисленных (и перечисляемых) типов, объявленных const, которые инициализируются константным выражением. Они по-прежнему считаются постоянными выражениями.
Итак, это:
const auto b = foo(0);
Если foo(0)
— целочисленное константное выражение, то b
— это то, что можно использовать в качестве константы времени компиляции (и оно было бы инициализировано константой†, если бы оно находилось в пространстве имен). Итак, что здесь происходит, так это то, что мы делаем двухэтапный анализ. Сначала мы пытаемся вычислить foo(0)
, как если бы это было постоянное выражение, а затем, если это не удается, возвращаемся к тому, чтобы не делать этого.
В этом первом синтаксическом анализе, когда foo(0)
оценивается как константа, is_constant_evaluated()
является (по определению) true
, поэтому мы получаем 1
. Этот синтаксический анализ завершается успешно, поэтому мы получаем b
в качестве константы времени компиляции.
†Для переменных пространства имен инициализация констант также является важной концепцией, чтобы избежать фиаско статического порядка инициализации. Это приводит к другим ужасным примерам (см. P0595).
Важная вещь здесь в основном: is_constant_evaluated()
следует включать только для выбора безопасного во время компиляции алгоритма по сравнению с алгоритмом времени выполнения, чтобы фактически не влиять на семантику результата.
Как он может вернуть 1 вместо этого? Если функция не объявлена
constexpr
, она не может быть частью постоянной оценки.