std::is_constant_evaluated 行为
std::is_constant_evaluated behavior
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
或没有。
我说得对吗?
if constexpr
需要条件的常量表达式。所以 is_constant_evaluated
在这种情况下当然总是正确的。
它适用于常规 if
。目的是在常量表达式中计算时不进入 constexpr
函数中非法的代码路径。而是让它在运行时执行。它不是为了从函数中完全消除这些代码路径。
以下是我对此的看法,也许您会发现这有帮助……也许不会。请注意,我认为写 if constexpr (std::is_constant_evaluated())
将是一个非常常见的错误,而且很容易掉入陷阱。但希望编译器能够诊断出这种情况。已提交 91428,已为 gcc 10.1 修复。
我们基本上有两种不同的代码规则 - 正常运行时代码的典型规则,以及用于 constexpr
编程的常量表达式的限制。这些是 expr.const 限制:没有 UB,没有 reinterpret_cast
,等等。这些限制从语言标准到语言标准不断减少,这很好。
基本上,控制流(从代码路径的角度来看)在 "full runtime" 模式和 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()
是假的。 (然后 test6
是一个不幸的语言特例 - 您可以使用常量整数变量作为常量表达式,因此我们出于这些目的以相同的方式处理它们的初始化。)
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
或没有。
我说得对吗?
if constexpr
需要条件的常量表达式。所以 is_constant_evaluated
在这种情况下当然总是正确的。
它适用于常规 if
。目的是在常量表达式中计算时不进入 constexpr
函数中非法的代码路径。而是让它在运行时执行。它不是为了从函数中完全消除这些代码路径。
以下是我对此的看法,也许您会发现这有帮助……也许不会。请注意,我认为写 if constexpr (std::is_constant_evaluated())
将是一个非常常见的错误,而且很容易掉入陷阱。但希望编译器能够诊断出这种情况。已提交 91428,已为 gcc 10.1 修复。
我们基本上有两种不同的代码规则 - 正常运行时代码的典型规则,以及用于 constexpr
编程的常量表达式的限制。这些是 expr.const 限制:没有 UB,没有 reinterpret_cast
,等等。这些限制从语言标准到语言标准不断减少,这很好。
基本上,控制流(从代码路径的角度来看)在 "full runtime" 模式和 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()
是假的。 (然后 test6
是一个不幸的语言特例 - 您可以使用常量整数变量作为常量表达式,因此我们出于这些目的以相同的方式处理它们的初始化。)