struct Foo
{
struct Bar
{
int data = 0;
//constexpr Bar() = default; // Doesn't work either
constexpr Bar() : data(0) {}
};
static constexpr Bar bar = {}; // ERROR
//static constexpr Bar bar = {0}; // Works if the ctor from Bar is removed
};
Clang 和 GCC(使用 std=c++20)说我尝试使用的构造函数未定义。但如果我将
constexpr
更改为 inline
,它就会起作用。我想了解使用“contexpr”有什么问题。
编译器分两遍解析一个类。第一遍处理成员变量和函数声明,第二遍处理函数主体,这允许您在该函数中使用在函数下面定义的成员变量。
当类嵌套时,第一遍遍历整个外部类(包括所有嵌套类),然后类似地第二遍遍历整个外部类(包括嵌套类)。
显然,
constexpr
静态变量初始化发生在第一遍期间,而非constexpr
(inline
)静态变量初始化发生在第二遍期间。
(上一段纯粹是根据编译器的行为方式进行的猜测。不确定这里的标准是什么,但这就是 GCC 和 Clang 的运行方式。)
当你摆脱构造函数并执行
= {0}
时,即聚合初始化,它根本不使用构造函数,因此可以在第一遍中使用。
您看到的错误与
static constexpr Bar bar
内的 struct Foo
成员变量的初始化有关。当您定义 constexpr
变量时,初始化器必须是 constant expression
,这是比简单的 compile-time constant
更严格的要求。
在您的情况下,
Bar
的默认构造函数使用成员初始化语法将data
初始化为0
。虽然这看起来像一个常量表达式,但实际上它不符合 C++ 标准对常量表达式的要求。
要初始化
constexpr
变量,用于初始化它的表达式必须是常量表达式。让代码与 constexpr
配合使用的一种方法是将 Bar
的默认构造函数更改为 constexpr constructor
,如下所示:
struct Bar
{
int data = 0;
constexpr Bar() = default;
};
通过此更改,您应该能够将
bar
定义为 constexpr
变量:
static constexpr Bar bar = {};
请注意,只有当您使用支持非聚合类的
constexpr
构造函数(C++17 中引入)的编译器时,此更改才有效。如果您使用的是旧版本的 C++ 标准,则需要坚持使用 inline
关键字而不是 constexpr
。