#include <iostream>
struct NonConstant{
NonConstant(int v):v_(v){
std::cout<<"NonConstant\n";
}
int v_;
};
struct Constant{
constexpr Constant(int v):v_(v){
if(v_==0){
std::cout<<"Constant\n";
}
}
int v_;
};
NonConstant a = 2; //#1
Constant b = 0; //#2
int main(){
}
outcome将是:
NonConstant
Constant
我对这个结果感到困惑,因为,根据标准规则,
#1
不是静态初始化,#2
是静态初始化,因为这些原因:constexpr未指定
NonConstant
类的构造函数,NonConstant a = 2;
的初始化将为对象a
调用一个非constexpr构造函数,因此,#1
的初始化不是静态初始化,因此是动态初始化。相反,由于要调用的构造函数是constexpr构造函数,因此Constant b = 0;
的初始化是静态初始化。规则说所有静态初始化都强烈地发生在任何动态初始化之前。那么,为什么结果暗示#1
的评估发生在#2
的评估之前?如果我错过了什么,请纠正我。更新:
在此问题的以下注释中,有人说,除了构造函数的类可以是非文字类型之外,constexpr构造函数在任何方面都必须是有效的核心常量表达式,也就是说,对
std::cout
的调用将使constexpr构造函数不会是核心常量表达式。但是,我在cppreference中找到了另一种解释,即:并没有说constexpr构造函数必须是一个核心常量表达式。只要调用构造函数即可满足
constexpr
的要求,并且其参数都必须是常量表达式,而member-initializer必须是常量表达式。因此,#2
确实是一个常量初始化,因为0
参数是一个常量表达式,并且由说明符constexpr
限定的所选构造函数和成员初始化器遵循expr.const中提到的这些规则。 最佳答案
b
具有动态初始化,而不具有静态初始化。
正如您对[basic.start.static]/2的引用所解释的那样,只有在其初始化程序的完整表达(即b
构造函数的执行)是常量表达式时,Constant(int)
才具有静态初始化。
在[expr.const]/2中,我们读取:
在这里,“遵循抽象机规则”对构造函数的评估包括构造函数主体。并且由于初始化程序是0
,因此该评估将调用std::operator<<(std::ostream&, const char*)
,而不是constexpr
。因此,初始化程序的完整表达式不是核心常量表达式,也不是常量表达式。
当然,尽管这不是严格的技术定义,但“常量表达式”的全部要点是定义何时可以保证编译器可以在编译时处理某些内容。而且在编译时肯定不会写入程序的标准输出。
cppreference.com是一个很好的资源,它试图尽可能地准确,但不能替代实际标准的权威。关于使用类构造函数进行常量初始化的报价对于C++ 14和C++ 17是不正确的。我怀疑它实际上是C++ 11遗留下来的,其中constexpr
构造函数的主体根本无法评估任何函数调用,[expr.const]类似地描述了在核心常量表达式中使用constexpr
构造函数的要求在成员初始化器方面。