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 的第一个语句之后的某个时间点,它应该发生在第一次使用与要初始化的对象在同一翻译单元中定义的任何函数或对象之前。
但是什么是“命名空间范围的对象”?我在标准中没有找到任何明确的定义。 scope 实际上是 name 的属性,而不是 object 的属性。因此,我们可以将其理解为“在命名空间范围内定义的对象”或“由命名空间范围的名称引入的对象”。注意动态初始化后的参考“9.4”。它指的是“静态成员”,只能表示静态数据成员。所以我会说它的意思是“在命名空间范围内定义的对象”,因为静态数据成员是在命名空间范围内定义的:
[class.static.data]/2
静态数据成员的定义应出现在包含该成员的类定义的命名空间范围内。
即使您不同意这种解释,仍然有
[basic.start.init]/1
在同一个翻译的命名空间范围内定义的具有静态存储持续时间的对象
单元和动态初始化应按照其定义在翻译单元中出现的顺序进行初始化。
这显然适用于静态数据成员,这意味着如果在静态数据成员的定义之前存在这样的对象,则它们的初始化方式不能不同于命名空间范围的名称引入的对象。也就是说,如果根本无法保证静态数据成员的动态初始化,则任何前面的由命名空间范围的名称引入的对象的保证都将适用——它们是:无(它不必须在main的第一条语句之前初始化)。
如果在静态数据成员的定义之前没有这样的对象并且您不同意解释 - 根本无法保证静态数据成员的动态初始化。
结论
因此,我们只能保证动态初始化在某个时间(在任何使用之前)发生,以及不能消除带有副作用的初始化的异常。尽管如此,我们仍不能保证在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;
}