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

时间:2013-04-30 12:55:04

标签: c++ language-lawyer c++03

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

2 个答案:

答案 0 :(得分:11)

我认为没有

  

[C++03: 3.6.2/3]: 实现定义是否在命名空间作用域对象的动态初始化(8.5,9.4,12.1,12.6.1)之前完成main的第一个语句。如果初始化被推迟到main的第一个语句之后的某个时间点,它应该在第一次使用同一个函数或对象之前发生。翻译单元作为要初始化的对象。


嗯,真的

嗯,可以说,“在命名空间范围内定义”与“命名空间范围的对象”并不完全相同:

  

[C++03: 9.4.2/2]:在其类定义中声明static数据成员不是定义,除了cv-qualified void之外,其类型可能不完整。 static数据成员的定义应出现在包含成员类定义的命名空间范围中。在命名空间范围的定义中,static数据成员的名称应使用::运算符通过其类名进行限定。 初始值设定项   静态数据成员定义中的表达式在其类的范围内(3.3.6)。

然而,它是类的范围内的初始值设定项;没有提到static成员本身有除名称空间范围之外的任何内容(除非我们在心理上注入“lexically”这个词)。

这个令人愉快的段落:

  

[C++03: 9.4.2/7]:静态数据成员的初始化和销毁​​与非本地对象完全一样(3.6.2,3.6.3)。

然而,遗憾的是,关于“非本地对象”,main和静态初始化的排序的唯一进一步定义是前面提到的[C++03: 3.6.2/3]


那么呢?

我相信C ++ 11中的新措辞清楚地表明了这个可能含糊不清的规则的意图,该措辞解决了所有问题:

  

[C++11: 9.4.2/6]:静态数据成员的初始化和销毁​​与非局部变量(3.6.2,3.6.3)完全相同。

     

[C++11: 3.6.2/4]:实现定义是否在第一个main语句之前完成具有静态存储持续时间的非局部变量的动态初始化。 [..]

答案 1 :(得分:4)

C ++ 03:简而言之,不保证

C ++ 11:不能保证,请参阅Lightness的回答。

我对C ++ 03陈述的解释/分析:


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

  

使用常量表达式进行零初始化和初始化统称为静态初始化;所有其他初始化都是动态初始化。


非本地对象的初始化顺序:

  

具有静态存储持续时间(3.7.1)的对象应在进行任何其他初始化之前进行零初始化(8.5)。

但它没有提及“任何其他初始化”何时发生,即不能保证它将在main的第一个语句之前,即使是零初始化

  

在使用常量表达式(5.19)初始化静态存储持续时间的POD类型(3.9)的对象应在进行任何动态初始化之前进行初始化。

但同样,不能保证。


动态初始化

[basic.start.init] / 3

  

是否实现定义了命名空间作用域对象的动态初始化(8.5,9.4,12.1,12.6.1)是否在main的第一个语句之前完成。如果初始化被推迟到main的第一个语句之后的某个时间点,它应该在第一次使用与要初始化的对象相同的转换单元中定义的任何函数或对象之前发生。

但什么是“命名空间范围的对象”?我没有在标准中找到任何明确的定义。 范围实际上是名称的属性,而不是对象的属性。因此,我们可以将其读作“在命名空间范围中定义的对象”或“由命名空间范围名称引入的对象”。动态初始化后请注意参考“9.4”。它指的是“静态成员”,它只能表示静态数据成员。所以我说它意味着“在命名空间范围内定义的对象”,因为静态数据成员是在命名空间范围内定义的:

[class.static.data] / 2

  

静态数据成员的定义应出现在包含成员类定义的命名空间范围内。

即使你不同意这种解释,仍然存在 [basic.start.init] / 1

  

具有静态存储持续时间的对象在同一转换中的命名空间作用域中定义   单位和动态初始化应按其定义出现在翻译单位的顺序进行初始化。

这显然适用于静态数据成员,这意味着如果在静态数据成员的定义之前存在这样的对象,则它们不能以与命名空间作用域名称引入的对象不同地进行初始化。也就是说,如果对静态数据成员的动态初始化没有任何保证,那么由名称空间范围引入的任何前面的对象的保证将适用 - 这些是:none(它不是必须在第一个主要声明之前进行初始化。

如果在静态数据成员的定义之前没有此类对象,则您对解释不一致 - 根本无法保证静态数据成员的动态初始化。


结论

因此,我们只保证动态初始化在某个时间(在任何使用之前)发生,加上一个异常,即不得消除带副作用的初始化。尽管如此,我们无法保证在main的第一个语句之前执行任何类型的初始化非本地对象。


注意:有一些解决方法,例如:

#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;
}