静态初始化的顺序是实现定义的吗?
我看到很困惑的答案,所以让我总结一下:不,静态初始化顺序未定义。
让我们看看为什么以及在哪些情况下。根据您的报价,我假设您正在询问非局部变量静态存储持续时间的(静态或动态)初始化。
C++ standard§9.4.2(第 6 段)说:
静态数据成员的初始化和销毁与非局部变量完全一样。
那么根据 §3.6.2 和 §3.7.1 不,编译单元内的 [order] 不是实现定义的,但它始终遵循声明顺序(如在 §6.7 第 4 段中描述):
在单个翻译单元中定义的具有有序初始化的变量应按照它们在翻译单元中的定义顺序进行初始化。
静态和动态初始化的规则是相同的,它们在同一段落中进行了描述,静态和动态初始化之间唯一(显着)的区别是排序,静态初始化总是在动态初始化之前发生,而动态初始化可能是无序的。
零初始化和常量初始化合称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前执行。
如何初始化静态初始化的非局部变量在 §3.6.2 的 1 和 2 段中描述:
具有静态存储持续时间 (3.7.1) 或线程存储持续时间 (3.7.2) 的变量应在任何其他初始化发生之前进行零初始化 (8.5)。执行常量初始化...[省略]
根据 cmets 我需要澄清这一点:非局部静态变量可能具有线程存储持续时间说明符 thread_local(然后它的行为就像来自用户 POV 的 static 变量坚果它们也可以组合在一起)。它仍然有资格进行静态初始化(然后排序,然后必须遵守上述规则),但不能简单地从磁盘加载数据段来执行初始化。必须执行代码(在 Windows 上为TlsAlloc),但从标准 POV 来看,它仍然是一个静态初始化表达式(首先零初始化,然后进行常量初始化)。让我们看一个非常虚构的例子:
thread_local unsigned int _value = 1;
现在让我们试着想象一下编译器是如何实现的。我们可以从已知的东西开始,想象类似于boost::thread_specific_ptr 的行为。在 Windows 上的实现(但在 Windows 上也非常相似)需要在线程初始化时调用 TlsAlloc,在线程完成时调用 TlsFree。您可以正常访问变量,但它可能会实现为一个指针,该指针与TlsAlloc 分配的内存有一个偏移量。唯一来自磁盘上的可执行文件的是初始值(同样,它只是一个你不应该关心的实现细节)。初始值(如前所述)将为 0(对于unsigned int)。鉴于所有这些代码、这些函数调用和如此多的细节......可能它仍然有资格被视为常量初始化,因为满足标准强加的要求,然后编译器应该(可能)尊重它们。这意味着它可能是静态初始化的。总结一下:
- 标准说静态初始化变量将遵守声明顺序。
- 标准说明变量何时动态初始化和何时静态初始化。
- 标准没有说明这些值在运行时应该来自哪里。
- 标准没有说明实现应该如何初始化它们。
当然,在单个函数中同样的规则适用,但 declaration 应该替换为 usage (§6.7 ) 但这不是你的问题。
不授予不同编译单元的顺序,并且定义了它的实现(但有一些技术可以使这个顺序任意然后可预测:Nifty Counter 和变量聚合,例如)。