静态 constexpr 数据成员的初始化顺序

tml*_*len 3 c++ static constexpr

在这样的代码中:

#include <iostream>

template<int I>
struct A {
    static constexpr int I1 = I + 1;
    static constexpr int I2 = I1 + 1;
};

int main() {
    std::cout << A<1>::I1 << " " << A<1>::I2 << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

是否可以安全地假设I2将正确初始化,即I1之前已初始化I2

Jan*_*tke 7

初始化顺序对于任何constexpr. 变量constexpr必须由常量表达式初始化,并且由于常量表达式没有任何副作用,并且可变全局状态在编译时不存在,因此您不必担心初始化顺序。

如果您想了解具体情况:

如果具有静态或线程存储持续时间的变量或临时对象被常量初始化([expr.const]),则执行常量初始化。[...]零初始化常量初始化一起称为静态初始化;所有其他初始化都是动态初始化。所有静态初始化都强烈发生在([intro.races])任何动态初始化之前。

- https://eel.is/c++draft/basic.start.static#2

静态初始化的初始化顺序并不重要,因为所有内容都被初始化为编译时常量。这只是动态初始化的问题。

对于常量初始化,唯一重要的是定义的顺序:

static constexpr int I1 = I + 1;  // I1 defined before I2
static constexpr int I2 = I1 + 1; // I2 can use this definition
Run Code Online (Sandbox Code Playgroud)

任何constexpr变量(包括static constexpr数据成员)都必须在定义它们的地方进行初始化。代码中出现的任何常量表达式都不可避免地具有可用的定义,因为程序是从上到下编译的。在这个例子中,这意味着:

  • I1必须始终在声明它的地方定义,因为它是constexpr
  • I2总是有这个定义可用,因为它出现在代码的更下方