在第一种情况下,您的程序在 main 启动之前崩溃了。它在构造函数内崩溃ta
因为它访问tb
尚未建成。这是一种形式静态初始化顺序惨败
第二种情况之所以成功是因为ta
在里面main
,这保证了tb
这是非本地的,之前构建的ta
.
问题是,为什么在第一种情况下,ta
之前建造过tb
虽然tb
and ta
在同一个编译单元中定义,tb
之前定义的ta
?
知道tb
是模板静态数据成员,cppreference 中的这句话适用: http://en.cppreference.com/w/cpp/language/initialization
动态初始化
全部静态初始化完成后,
非局部变量的动态初始化发生在以下情况
情况:
1)无序动态初始化,仅适用于(静态/线程局部)变量模板和(C++11 起)类模板静态数据成员没有明确专门化的。此类静态变量的初始化相对于所有其他动态初始化的顺序是不确定的除非程序在初始化变量之前启动线程,在这种情况下其初始化是无序的 (C++17 起)。此类线程局部变量的初始化相对于所有其他动态初始化是无序的。
所以这里不保证顺序!自从ta
是一个静态变量explicit模板特化,允许编译器在之前对其进行初始化tb
.
同一页面的另一句话说:
早期动态初始化
如果满足以下条件,则允许编译器将动态初始化变量初始化为静态初始化的一部分(本质上是在编译时):
1) 动态版本的初始化不会在初始化之前更改名称空间范围内任何其他对象的值
2) 如果所有不需要静态初始化的变量都被动态初始化,静态版本的初始化在初始化变量中生成的值与动态初始化生成的值相同。由于上述规则,如果某个对象 o1 的初始化引用了命名空间范围的对象 o2,该对象可能需要动态初始化,但稍后在同一翻译单元中定义,则未指定所使用的 o2 的值是否为该值完全初始化的 o2 的值(因为编译器将 o2 的初始化提升到编译时),或者只是将 o2 的值初始化为零。
编译器决定根据这些规则来促进初始化ta
before tb
。是否被提升为静态初始化尚不清楚,但无论如何,很明显,当涉及到变量模板和静态模板成员时,初始化的顺序并不能得到保证,因为第一个引用和 的提升规则第二个报价。
Solution
为了保证tb
在使用之前进行初始化,最简单的方法是将其放入包装函数中。我认为这应该是处理静态模板成员时的经验法则:
template<typename T>
class TA{
//...
static TB<T>& getTB();
};
template<typename T>
TB<T>& TA<T>::getTB()
{ static TB<T> tb("static-tb");
return tb;
}