【问题标题】:Is initialization order guaranteed初始化顺序是否有保证
【发布时间】:2013-08-27 19:53:45
【问题描述】:

我正在使用类似于以下代码部分的代码来进行一些初始化。我知道p<T>::i_的初始化是无序的。我相信h 是有序的,所以我应该能够推断出它的初始化顺序。鉴于p 的标头包含在h 的定义之前,是否可以保证@987654325 @会在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_;

【问题讨论】:

  • 这不会编译 - helper 没有默认构造函数。
  • 用什么构造函数来初始化h?
  • “我相信h 已订购”。与什么相比排序?

标签: c++ c++11


【解决方案1】:

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

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

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

【讨论】:

  • +1 用于提及翻译单元/链接器参与。我想知道模板中的static 非模板变量是否在模板实例化时被初始化?或者不管模板是否被实例化,它都会被初始化(看起来有点奇怪)?
  • 重要的是模板的隐式实例化不会触发静态成员的初始化。当以需要定义的方式使用它们时,它们会被初始化。
  • @PetrBudnik:除非您显式实例化模板,否则所有成员都会按需实例化。如果程序不使用模板的静态成员,除非使用该成员,否则编译器不会实例化该成员。
  • @PetrBudnik: 是的,在构造函数中调用h 将导致p&lt;ConcreteType&gt;::i_ 的实例化,但这不一定是@987654328 的only 实例化@。如果不同的翻译单元包含helper 的类型helper2 的克隆,并且它创建了一个具有静态存储持续时间的变量h2,触发了p&lt;ConcreteType&gt;::i_ 的实例化,那么两个翻译单元都会实例化它。链接器将删除其中之一。对于保留自己的实例的翻译单元,顺序是有保证的,但对其他的[...] 没有保证
  • [...],也就是说它会删除helper中的版本,最终的顺序可能是h &gt; p&lt;CT&gt;::i_ &gt; h2。在h 的初始化期间,将访问尚未初始化的p&lt;CT&gt;::i_,从而导致未定义的行为。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    不,不能保证。

    但是你可以做的是:

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

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

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

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

    【讨论】:

    • 调试动态初始化可能没有你想象的那么难。有一个依赖于平台的符号,你可以打破它,它标志着动态初始化的入口,然后你可以像任何函数一样单步执行它。
    • @user1131467:我发现调试工具在初始化和关机期间无法正常工作。请注意,在初始化期间,甚至不清楚有多少标准库已经初始化并且可以使用,以及在静态持续时间实例的销毁期间已经关闭了多少标准库。除此之外,例如在某些情况下,Windows 在退出应用程序时会默默吞下段错误......
    猜你喜欢
    • 1970-01-01
    • 2016-04-16
    • 2020-09-11
    • 1970-01-01
    • 2011-03-02
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    • 2012-02-16
    相关资源
    最近更新 更多