简体   繁体   English

静态类成员是否保证在调用`main`之前初始化?

[英]Are static class members guaranteed to be initialized before `main` is called?

是否可以保证在调用main之前初始化静态类成员?

I think no : 我认为不是

[C++03: 3.6.2/3]: It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope is done before the first statement of main . [C++03: 3.6.2/3]: 无论 命名空间作用域对象 的动态初始化 (8.5,9.4,12.1,12.6.1) 是否在main的第一个语句之前完成, 它都是实现定义的 If the initialization is deferred to some point in time after the first statement of main , it shall occur before the first use of any function or object defined in the same translation unit as the object to be initialized. 如果初始化延迟到main的第一个语句之后的某个时间点,则它应该在第一次使用与要初始化的对象相同的转换单元中定义的任何函数或对象之前发生。


Hmm, really ? 嗯, 真的吗?

Well, arguably, "defined in namespace scope" is not quite the same thing as "an object of namespace scope": 好吧,可以说,“在命名空间范围内定义”与“命名空间范围的对象”并不完全相同:

[C++03: 9.4.2/2]: The declaration of a static data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void . [C++03: 9.4.2/2]: static数据成员在其类定义中的声明不是定义,除了cv-qualified void之外可能是不完整的类型。 The definition for a static data member shall appear in a namespace scope enclosing the member's class definition. static数据成员的定义应出现在包含成员类定义的命名空间范围内 In the definition at namespace scope, the name of the static data member shall be qualified by its class name using the :: operator. 在命名空间作用域的定义中, static数据成员的名称应使用::运算符通过其类名限定。 The initializer expression in the definition of a static data member is in the scope of its class (3.3.6). 静态数据成员定义中的初始化表达式在其类的范围内(3.3.6)。

However, it's the initializer that's in the class's scope; 但是,它是类的范围内的初始化器 ; there's no mention of the static member itself having anything other than namespace scope (unless we mentally inject the word "lexically" everywhere). 没有提到static成员本身具有除命名空间范围之外的任何东西(除非我们精神上在任何地方注入“词汇”一词)。

There is this pleasing paragraph: 这种令人愉悦的一段话:

[C++03: 9.4.2/7]: Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3). [C++03: 9.4.2/7]:静态数据成员的初始化和销毁​​与非本地对象完全相同(3.6.2,3.6.3)。

However, unfortunately, the only further definition of the sequencing of main and static initialisation, with respect to "non-local objects", is the aforementioned [C++03: 3.6.2/3] . 然而,遗憾的是,关于“非本地对象”的main和静态初始化的排序的唯一进一步定义是上述[C++03: 3.6.2/3]


So what then? 那么呢?

I believe that the intent of this otherwise potentially ambiguous rule is clearly shown by the new wording in C++11, which resolves everything: 我相信C ++ 11中的新措辞清楚地表明了这个可能含糊不清的规则的意图,它解决了所有问题:

[C++11: 9.4.2/6]: Static data members are initialized and destroyed exactly like non-local variables (3.6.2, 3.6.3). [C++11: 9.4.2/6]:静态数据成员的初始化和销毁​​与非局部变量(3.6.2,3.6.3)完全相同。

[C++11: 3.6.2/4]: It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. [C++11: 3.6.2/4]:实现定义了具有静态存储持续时间的非局部变量的动态初始化是否在main的第一个语句之前完成。 [..] [..]

C++03: In short, no guarantee C ++ 03:总之,不能保证

C++11: No guarantee, see Lightness' answer. C ++ 11:不能保证,请参阅Lightness的回答。

My interpretation/analysis of the C++03 statements: 我对C ++ 03语句的解释/分析:


Terminology: [basic.start.init]/1 术语:[basic.start.init] / 1

Zero-initialization and initialization with a constant expression are collectively called static initialization; 使用常量表达式进行零初始化和初始化统称为静态初始化; all other initialization is dynamic initialization. 所有其他初始化是动态初始化。


Order of initialization on non-local objects: 非本地对象的初始化顺序:

Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. 具有静态存储持续时间(3.7.1)的对象应在任何其他初始化发生之前进行零初始化(8.5)。

But it doesn't mention when "any other initialization" takes place, ie there's no guarantee it'll be before the first statement of main, even for zero-initialization . 但它没有提到“任何其他初始化”何时发生,即不能保证它将在main的第一个语句之前, 即使是零初始化

Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place. 具有使用常量表达式(5.19)初始化的静态存储持续时间的POD类型(3.9)的对象应在任何动态初始化发生之前初始化。

But again, no guarantee. 但同样,不能保证。


Dynamic initialization 动态初始化

[basic.start.init]/3 [basic.start.init] / 3

It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope is done before the first statement of main. 它是实现定义的,无论命名空间作用域的对象的动态初始化(8.5,9.4,12.1,12.6.1)是否在main的第一个语句之前完成。 If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first use of any function or object defined in the same translation unit as the object to be initialized. 如果初始化延迟到main的第一个语句之后的某个时间点,则它应该在第一次使用与要初始化的对象相同的转换单元中定义的任何函数或对象之前发生。

But what is an "object of namespace scope"? 但什么是“命名空间范围的对象”? I have not found any clear definition in the Standard. 我没有在标准中找到任何明确的定义。 scope is actually a property of a name , not of an object . scope实际上是名称的属性,而不是对象的属性 Therefore we could read this as "object defined in namespace scope" or "object introduced by a name of namespace scope". 因此,我们可以将其读作“在命名空间范围中定义的对象”或“由命名空间范围名称引入的对象”。 Note the reference "9.4" after dynamic initialization. 动态初始化后请注意参考“9.4”。 It refers to "Static members", which can only mean static data members . 它指的是“静态成员”,它只能表示静态数据成员 So I'd say it means "object defined at namespace scope", as static data members are defined at namespace scope: 所以我说它意味着“在命名空间范围内定义的对象”,因为静态数据成员是在命名空间范围内定义的:

[class.static.data]/2 [class.static.data] / 2

The definition for a static data member shall appear in a namespace scope enclosing the member's class definition. 静态数据成员的定义应出现在包含成员类定义的命名空间范围内。

Even if you don't agree on this interpretation, there's still [basic.start.init]/1 即使你不同意这种解释,仍然有[basic.start.init] / 1

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit. 在同一翻译单元的命名空间范围内定义并动态初始化的静态存储持续时间的对象应按其定义出现在翻译单元中的顺序进行初始化。

This clearly applies to static data members, which means that they cannot be initialized differently than objects introduced by names of namespace scope if there's such an object before the definition of the static data member. 这显然适用于静态数据成员,这意味着如果在静态数据成员的定义之前存在这样的对象,则它们不能与命名空间作用域名称引入的对象进行不同的初始化。 That is, if there was no guarantee at all on the dynamic initialization of static data members, the guarantees of any preceding object introduced by a name of namespace scope would apply - which are: none (it does not have to be initialized before the first statement of main). 也就是说,如果对静态数据成员的动态初始化完全没有保证,那么由名称空间作用域引入的任何前面对象的保证都将适用 - 它们是:none(它不需要在第一个之前初始化)主要声明)。

If there's no such object preceding the definition of the static data member and you disagree on the interpretation - there would be no guarantee on the dynamic initialization of static data members at all. 如果在静态数据成员的定义之前没有这样的对象, 并且您对解释不一致 - 根本不能保证静态数据成员的动态初始化。


Conclusion 结论

So we only have a guarantee that dynamic initialization happens sometime (before any usage) plus an exception that initialization with side-effects must not be eliminated. 因此,我们只保证动态初始化在某个时间(在任何使用之前)发生,加上一个异常,即不得消除带副作用的初始化。 Still, we have no guarantee that any kind of initialization of non-local objects is performed before the first statement of main . 但是,我们无法保证在第一个main语句之前执行任何类型的非本地对象初始化


Note: There are workarounds, like: 注意:有一些解决方法,例如:

#include <iostream>

struct my_class
{
    static int& my_var()
    {
        static int i = 42;
        return i;
    }
};

int j = ++my_class::my_var();
int k = ++my_class::my_var();

int main()
{
    std::cout << j << " : " << k << std::endl;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM