C ++静态变量初始化顺序 [英] C++ static variables initialization order

查看:103
本文介绍了C ++静态变量初始化顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

1) If I'm not mistaken, C++ standard guarantees that static variables in a single translation unit are initialized in their definition order. And I'm confused about the following code fragment:

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

extern int n;是声明,而不是定义,因此k是在n之前定义的,但是GCC,Clang和MSVC都向我显示了在初始化全局变量之后的k == 2.对我来说,目前尚不清楚如何在int k = n;之后为k分配2,因为n当时尚未初始化,并且其值必须为零.

extern int n; is the declaration, not the definition, so k is defined before n, but GCC, Clang and MSVC all show me that k == 2 after the initialization of the global variables. For me it's unclear how can k be assigned 2 after int k = n;, because n is not initialized at that point yet and its value must be zero.

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

If we change the last line to:

int n = func();

其中,func()是非constexpr,那么,正如我期望的那样,k将被分配零.那么,在编译时对全局变量的初始化会改变初始化的顺序吗?

where func() is non-constexpr, then k will be assigned zero, as I expect. So, do initialization of the global variables at the compile time change the order of the initialization?

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

2) Here is another code fragment:

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?

When Base::constructor is defined, its constructor is called, and i = 1 assignment is performed. But at this point Base::i is not defined yet, so, please, could you explain me what happens at this point and why is Base::i equal to 1 in the end?

推荐答案

第一种情况在[basic.start.init]/2中定义明确:

The first scenario is well-defined in [basic.start.init]/2:

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

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.

恒定初始化被执行:

  • 如果出现在具有静态或线程存储持续时间的引用的初始化程序中的每个完整表达式(包括隐式转换)是一个常量表达式(5.19),并且该引用绑定到一个指定具有静态存储持续时间的对象的左值或到临时位置(请参见12.2);
  • 如果具有静态或线程存储持续时间的对象是通过构造函数调用初始化的,如果构造函数是constexpr构造函数,则所有构造函数参数都是常量表达式(包括转换),并且在函数调用替换之后(7.1) .5),对于非静态数据成员, mem-initializers brace-or-equal-initializers 中的每个构造函数调用和完整表达式都是常量表达式;
  • 如果具有静态或线程存储持续时间的对象未通过构造函数调用初始化,并且其初始化程序中出现的每个完整表达式都是常量表达式.
  • 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. (...)

(强调我的)

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

The upshot of this fairly lengthy paragraph is that

int n = 2;

静态初始化,而

int k = n;

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

is dynamic initialization (because n is not a constant expression), and therefore n is initialized before k even if it appears later in the code.

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

The same logic applies in the case of the Base::static_constructor example -- because the constructor of Base::static_constructor is not constexpr, Base::constructor is dynamically initialized, whereas Base::i is statically initialized. The initialization of Base::i therefore takes place before the initialization of Base::constructor.

另一方面,第二种情况

int n = func();

将您直接置于未指定行为的领域,[basic.start.init]/3中对此进行了明确提及:

puts you squarely in the territory of unspecified behavior, and it is quite explicitly mentioned in [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

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

[注意:结果是,如果对象obj1的初始化是指名称空间范围的对象obj2可能需要动态初始化并稍后在同一转换单元中定义,则它会未指定使用的obj2的值将是完全初始化的obj2的值(因为obj2是静态初始化的)还是将是仅零初始化的obj2的值.例如,

[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

-结束符]

这篇关于C ++静态变量初始化顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆