#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构造函数的要求在成员初始化器方面。

10-07 19:52
查看更多