4

我正在使用类似于以下代码部分的代码来进行一些初始化。我知道的初始化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_;
4

3 回答 3

6

具有静态存储持续时间的对象的初始化顺序在翻译单元之间是未定义的,并且在每个翻译单元内是连续的。

在您的特定情况下,事情会更加复杂,因为具有静态存储的对象之一是模板类的静态成员。这实际上意味着访问该成员的每个翻译单元都p<T>::i_将创建符号,并添加适当的初始化代码。稍后链接器将选择其中一个实例并保留它。即使它看起来像是之前p<T>::i_在您的翻译单元中定义的,您也不知道链接器将保留哪个实例,并且这可能是不同翻译单元中的一个,因此无法保证顺序。 hp<T>::i_

一般来说,拥有全局对象是个坏主意,我建议您尝试在没有这些全局对象的情况下重新设计程序。

于 2013-08-27T20:02:06.690 回答
5

全局或命名空间范围内的对象在一个翻译单元中从上到下构建。未定义不同翻译单元之间的全局或命名空间级别的构建顺序。在翻译单元之间排序初始化的最合理方法是将对象包装在合适的访问器函数中,例如:

template <typename T>
something<T>& get() {
    static something<T> values;
    return value;
}

但是请注意,这在 C++03 中不是线程安全的(因为 C++03 无论如何都没有线程的概念)。它在 C++11 中是线程安全的。

于 2013-08-27T20:01:32.893 回答
0

不,不能保证。

但是,您可以做的是:

template<typename T>
std::vector<int>& registry() {
    static std::vector<int> reg;
    return reg;
}

...
registry<T>().push_back(i);
...

更好的是避免在启动过程中做一些过于聪明的事情。

在开始之前或结束之后进行调试main是一场真正的噩梦(而且 IMO 甚至没有在标准中 100% 覆盖)。简单的注册可能没问题,但永远不要做任何可能失败的事情。

多年来,我从这种方法转向显式初始化/关闭,并且从未回头。

于 2013-08-27T20:02:42.427 回答