C++20 consteval 函数和 constexpr 变量 - 是否保证在编译时计算它们?

Ber*_*ard 3 c++ language-lawyer constexpr c++20 consteval

在 C++20 中,我们有consteval声明立即函数的关键字。例如:

consteval int f(int x) { return x * x; }
Run Code Online (Sandbox Code Playgroud)

需要这样的函数来生成常量表达式。但是,根据标准,常量表达式不需要在编译时实际求值,除非它用在需要常量的地方,例如在模板参数中。例如,标准中似乎没有任何内容要求在编译时对其进行评估:

int a = f(10);
Run Code Online (Sandbox Code Playgroud)

然而,这些要求强烈表明立即函数应该在编译时进行评估。

标准中似乎也没有要求在编译时评估 constexpr 变量。所以即使我们创建变量constexpr,即

constexpr int a = f(10);
Run Code Online (Sandbox Code Playgroud)

它只断言这f(10)是一个常量表达式并生成a一个常量表达式(但同样,常量表达式不需要在编译时实际评估它们)。然而,就像以前一样,对 constexpr 变量的要求强烈表明它们应该在编译时进行评估。

只有constinit 变量的定义不同 - constinit 变量需要进行静态初始化,因此必须在编译时计算它们并直接嵌入到二进制文件中。然而,这个相关问题的答案说 constexpr 意味着 constinit,至少对于全局变量来说,这似乎与我上面写的内容相矛盾。

那么, consteval 函数和 constexpr 变量是否保证在编译时进行求值?


旁注:我的实际用例涉及尝试使用常量初始化结构中的字段,如下所示:

consteval int my_complicated_calculation() {
    // do complicated mathematics and return an int
}
struct A {
    int value;
    A() : value{my_complicated_calculation()} {}
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 10

该标准没有“编译时”的概念。只有“常量求值”和“常量表达式”。实现可以实现“常量表达式”,以便它们在编译时被评估。

但是,在实际的 C++ 程序中,没有任何 C++ 标准定义的行为可以使“常量表达式”等同于编译时编译。编译器为常量表达式生成代码是 100% 合法的,而且一直如此。

来自[expr.const]/13

“立即调用[即:调用函数consteval]应是常量表达式

所以调用consteval函数就是常量表达式。这是否意味着编译器肯定不会为运行时调用的该函数发出实际的程序集?不; 这是执行质量的问题。

然而,常量表达式的某些用途确实会影响编译器的各种决策。这意味着,虽然编译器通常不必编译时计算它们,但如果在影响编译基本行为的地方使用该值,则编译器必须在该实例中立即计算表达式。

例如:

std::is_same_v<array<int, some_constexpr_func(3, 4)>, array<int, 7>>
Run Code Online (Sandbox Code Playgroud)

此(常量)表达式的计算结果是否为truefalse 取决于some_constexpr_func(3, 4)返回的内容。如果它返回其参数的总和,则为true。编译器需要知道这一点,因为它需要发出该std::array<int, S>类型的代码,该类型作为其类型的一部分包括其模板参数。所以它需要知道它们是什么。

除了常量求值影响编译器决策的情况外,编译时是否发生任何常量求值取决于编译器的质量。