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

C++ static variables initialization order

1) 如果我没记错的话,C++ 标准保证单个翻译单元中的静态变量按照它们的定义顺序进行初始化。我对以下代码片段感到困惑:

extern int n;
int k = n;
int n = 2;

extern int n;是声明,不是定义,所以k定义在n之前,但是GCC、Clang和MSVC都告诉我k == 2n之后全局变量的初始化。对我来说,不清楚 k 如何在 int k = n; 之后被赋值 2,因为 n 在那个时候还没有初始化,它的值必须为零。

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

int n = func();

其中 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;

定义Base::constructor时,调用其构造函数,进行i = 1赋值。但是此时 Base::i 还没有定义,所以,请你解释一下此时发生了什么,为什么 Base::i 最后等于 1?

第一个场景在 [basic.start.init]/2 中定义明确:

Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place.

Constant initialization is performed:

  • if each full-expression (including implicit conversions) that appears in the initializer of a reference with static or thread storage duration is a constant expression (5.19) and the reference is bound to an lvalue designating an object with static storage duration or to a temporary (see 12.2);
  • if an object with static or thread storage duration is initialized by a constructor call, if the constructor is a constexpr constructor, if all constructor arguments are constant expressions (including conversions), and if, after function invocation substitution (7.1.5), every constructor call and full-expression in the mem-initializers and in the brace-or-equal-initializers for non-static data members is a constant expression;
  • if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.

Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place. (...)

(强调我的)

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

int n = 2;

静态初始化,而

int k = n;

动态初始化(因为n不是常量表达式),因此nk之前初始化,即使它稍后出现在代码中。

相同的逻辑适用于 Base::static_constructor 示例的情况——因为 Base::static_constructor 的构造函数不是 constexprBase::constructor动态初始化,而Base::i静态初始化。因此 Base::i 的初始化发生在 Base::constructor.

的初始化之前

另一方面,第二种情况

int n = func();

直接将您置于未指定行为的范围内,并且在 [basic.start.init]/3:

中明确提及

An implementation is permitted to perform the initialization of a non-local variable with static storage duration as a static initialization even if such initialization is not required to be done statically, provided that

  • the dynamic version of the initialization does not change the value of any other object of namespace scope prior to its initialization, and
  • the static version of the initialization produces the same value in the initialized variable as would be produced by the dynamic initialization if all variables not required to be initialized statically were initialized dynamically.

[Note: As a consequence, if the initialization of an object obj1 refers to an object obj2 of namespace scope potentially requiring dynamic initialization and defined later in the same translation unit, it is unspecified whether the value of obj2 used will be the value of the fully initialized obj2 (because obj2 was statically initialized) or will be the value of obj2 merely zero-initialized. For example,

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

-- end note]