C++静态变量初始化顺序

und*_*ind 5 c++ initialization global-variables static-variables static-members

1)如果我没有弄错,C++标准保证单个翻译单元中的静态变量按其定义顺序初始化.我对以下代码片段感到困惑:

extern int n;
int k = n;
int n = 2;
Run Code Online (Sandbox Code Playgroud)

extern int n;是声明,而不是定义,所以k是之前定义的n,但是GCC,Clang和MSVC都告诉我k == 2全局变量初始化之后.对我来说,目前还不清楚如何k在之后分配2 int k = n;,因为n尚未在该点初始化,其值必须为零.

如果我们将最后一行更改为:

int n = func();
Run Code Online (Sandbox Code Playgroud)

哪里func()是非constexpr,然后k将被指定为零,正如我所期望的那样.那么,在编译时初始化全局变量会改变初始化的顺序吗?

2)这是另一个代码片段:

class Base
{
public:
    struct static_constructor
    {
        static_constructor()
        {
             i = 1;
        }
    };
    static static_constructor constructor;
    static int i;
};

Base::static_constructor Base::constructor;
int Base::i = 2;
Run Code Online (Sandbox Code Playgroud)

Base::constructor被定义,它的构造函数被调用,并i = 1进行分配.但是Base::i目前还没有定义,所以,请你解释一下此时发生了什么,为什么最后Base::i等于1?

Win*_*ute 6

第一个场景在[basic.start.init]/2中定义得很好:

具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的变量应在任何其他初始化发生之前进行零初始化(8.5).

执行常量初始化:

  • 如果在具有静态或线程存储持续时间的引用的初始值设定项中出现的每个完整表达式(包括隐式转换)是常量表达式(5.19)并且引用绑定到指定具有静态存储持续时间的对象的左值或临时(见12.2);
  • 如果具有静态或线程存储持续时间的对象由构造函数调用初始化,如果构造函数是constexpr构造函数,如果所有构造函数参数都是常量表达式(包括转换),并且在函数调用替换(7.1.5)之后,每个构造函数mem-initializers中的call和full-expression 以及非静态数据成员的brace-or-equal-initializers是一个常量表达式;
  • 如果具有静态或线程存储持续时间的对象未通过构造函数调用初始化,并且其初始化程序中出现的每个完整表达式都是常量表达式.

零初始化和常量初始化一起称为静态初始化 ; 所有其他初始化是动态初始化.在进行任何动态初始化之前,应执行静态初始化.(......)

(强调我的)

这个相当冗长的段落的结果是

int n = 2;
Run Code Online (Sandbox Code Playgroud)

静态初始化,而

int k = n;
Run Code Online (Sandbox Code Playgroud)

动态初始化(因为n它不是常量表达式),因此即使它稍后出现在代码中也会n被初始化k.

同样的逻辑适用于的情况下Base::static_constructor的例子-由于构造Base::static_constructor是不constexpr,Base::constructor动态地初始化,而Base::i静态初始化.因此初始化Base::i发生在初始化之前Base::constructor.

另一方面,第二种情况

int n = func();
Run Code Online (Sandbox Code Playgroud)

将你直接置于未指明行为的领域,并在[basic.start.init]/3中明确提到:

允许实现以静态存储持续时间的形式执行非局部变量的初始化作为静态初始化,即使这样的初始化不需要静态地完成,只要这样做,

  • 初始化的动态版本在初始化之前不会更改命名空间作用域的任何其他对象的值,并且
  • 初始化的静态版本在初始化变量中产生与动态初始化产生的值相同的值,如果所有不需要静态初始化的变量都是动态初始化的.

[ 注意:因此,如果对象的初始化obj1引用obj2了命名空间范围的对象,可能需要动态初始化并稍后在同一个转换单元中定义,则未指定obj2used的值是否为完全初始化的值obj2(因为obj2是静态初始化的)或者obj2只是零初始化的值.例如,

inline double fd() { return 1.0; }
extern double d1;
double d2 = d1;     // unspecified:
                    // may be statically initialized to 0.0 or
                    // dynamically initialized to 0.0 if d1 is
                    // dynamically initialized, or 1.0 otherwise
double d1 = fd();   // may be initialized statically or dynamically to 1.0
Run Code Online (Sandbox Code Playgroud)

- 结束说明 ]

  • 根据[dcl.constexpr] / 5,我看到:“如果不存在任何参数值,以致函数或构造函数的调用可能是核心常量表达式的求值子表达式...程序格式错误;无诊断性需要”; 核心常量表达式的定义:“条件表达式_e_是核心常量表达式,除非_e _...的求值将求...对象的修改,除非将其应用于非易失性左值...其寿命始于_e_”的评估。因此,修改全局变量的constexpr函数格式错误。 (2认同)