我正在使用类似以下代码段的内容来进行一些初始化。我知道p<T>::i_
的初始化是无序的。我相信h
是有序的,所以我应该能够推断出它被初始化的顺序。鉴于p
的标题包含在h
的定义之前,是是否保证在p<T>::i_
之前初始化h
?
struct helper
{
template <typename T>
helper(const T&, int i)
{
p<T>::i_::push_back(i);
}
};
static helper h;
p类定义如下。
template <typename T>
struct p
{
static std::vector<int> i_;
};
template <typename T>
std::vector<int> p<T>::i_;
答案 0 :(得分:6)
具有静态存储持续时间的对象的初始化顺序在翻译单元中是未定义的,并且在每个翻译单元中是连续的。
在您的特定情况下,事情会更复杂,因为其中一个具有静态存储的对象是模板类的静态成员。这实际上意味着访问成员p<T>::i_
的每个翻译单元将创建符号,并添加适当的初始化代码。稍后链接器将选择其中一个实例并保留它。即使在您的翻译单元中 p<T>::i_
之前定义了<{1>} h
,您也不知道链接器将保留p<T>::i_
的哪个实例,这可能是不同翻译单位中的一个,因此订单无法保证。
一般来说,拥有全局对象是一个坏主意,我建议您尝试重新设计没有这些全局变量的程序。
答案 1 :(得分:5)
全局或命名空间范围内的对象在一个转换单元中从上到下构建。未定义不同转换单元之间的全局或命名空间级别的构造顺序。在翻译单元之间进行初始化的最合理方式是将对象包装在合适的访问器函数中,例如:
template <typename T>
something<T>& get() {
static something<T> values;
return value;
}
但请注意,这在C ++ 03中不是线程安全的(因为C ++ 03无论如何都没有线程概念)。它在C ++ 11中是线程安全的。
答案 2 :(得分:0)
不,这不能保证。
但是你可以做的是:
template<typename T>
std::vector<int>& registry() {
static std::vector<int> reg;
return reg;
}
...
registry<T>().push_back(i);
...
更好的做法是避免在启动时做太聪明的事情。
在main
开始之前或结束之后进行调试是一场真正的噩梦(IMO甚至没有在标准中覆盖100%)。简单注册可能没问题,但是不要做任何可能失败的事情。
多年来,我从这种方法转向显式初始化/关闭,从未回头。