C++20 | std::is_constant_evaluate() 和 const 变量

Bkt*_*ero 3 c++ c++20

让我们考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

它用gcc 和 clang返回 0 。我原以为它会返回 1 。

如果foo()是 made constexpr, whileb被简单地保留const,那么它确实返回 1 。

我在这里缺少什么?谢谢!

Bar*_*rry 8

std::is_constant_evaluated()当且仅当[meta.const.eval]返回真:

调用的求值发生在明显是常量求值的表达式或转换的求值中

这个术语,“明显的常量评估”(在此处定义),指的是必须进行常量评估的上下文。对非constexpr函数(这里最近的封闭上下文)的调用永远不会被评估为常量,因为它是 non- constexpr,所以这直接不是“明显的常量评估”。

constexpr但是,一旦我们成功了,我们就会陷入这种奇怪的遗产怪癖中。在引入 的 C++11 之前constexpr,我们仍然可以做这样的事情:

template <int I> void f();

const int i = 42; // const, not constexpr
f<i>();           // ok
Run Code Online (Sandbox Code Playgroud)

基本上,我们为声明为 const 且使用常量表达式初始化的特定整型(和枚举)类型进行了划分。那些仍然算作常量表达式。

所以这:

const auto b = foo(0);
Run Code Online (Sandbox Code Playgroud)

Iffoo(0)是一个完整的常量表达式,然后b是可以用作编译时常量的东西(如果它在命名空间范围内,则会被常量初始化)。所以这里发生的是我们做一个两步解析。我们首先尝试将其foo(0)视为常量表达式进行评估,然后如果失败,则退回这样做。

在第一个解析中,foo(0)计算为常量,is_constant_evaluated()is (根据定义) true,所以我们得到1. 这个解析成功了,所以我们最终得到了b一个编译时常量。


对于命名空间范围的变量,常量初始化也是一个重要的概念 - 避免静态初始化顺序失败。它导致了其他粗糙的例子(见P0595)。

这里重要的事情基本上是:is_constant_evaluated()只应该打开以选择编译时安全算法与运行时算法,而不是实际影响结果的语义。


cig*_*ien 5

您必须小心使用的位置和方式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
        // ...
}
Run Code Online (Sandbox Code Playgroud)

另一个值得指出的常见错误是,is_constant_evaluatedif constexpr条件中也没有任何意义:

{
    if constexpr (std::is_constant_evaluated()) // pointless: always true
                                                // regardless of whether foo 
                                                // is run-time or compile-time
}
Run Code Online (Sandbox Code Playgroud)