简体   繁体   English

C ++模板:未初始化全局对象中的静态成员

[英]C++ template: The static member in a global object is not initialized

I have a piece of simple C++ code, in which I defined a template and a global object by specializing the template. 我有一段简单的C ++代码,其中我通过专门化模板来定义模板和全局对象。 The object constructor accesses a static member in the specialized template. 对象构造函数访问专用模板中的静态成员。 But it turns out the static member is not initialized at that point. 但事实证明静态成员在那时没有被初始化。 But for a local object (defined in the body of a function), it works. 但对于本地对象(在函数体中定义),它可以工作。 I'm confused... 我糊涂了...

My c++ compiler is: g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609 我的c ++编译器是:g ++(Ubuntu 5.4.0-6ubuntu1~16.04.4)5.4.0 20160609

/////////////////////////
template<typename T>
class TB{
public:
  const char *_name;
  TB(const char * str):_name(str){
    cout << "constructor is called:" << _name << endl;
  };

  virtual ~TB(){
    cout << "destructor is called:" << _name << endl;
  };
};

template<typename T>
class TA{
public:
  const char *_name;
  TA(const char * str):_name(str){
    cout << "constructor is called:" << _name << endl;
    cout << tb._name <<endl;
  };

  virtual ~TA(){
    cout << "destructor is called:" << _name << endl;
  };

  static TB<T> tb;
};

template<typename T>
  TB<T> TA<T>::tb("static-tb");
TA<int> ta("global-ta");

int main(int argc,char ** argv){
  cout << "program started." << endl;
  cout << "program stopped." << endl;
  return 0;
}

/////////////////////////
//  OUTPUT:
constructor is called:global-ta
// yes, only such a single line.

If I put the definition of ta in main() like the following, it works. 如果我把ta的定义放在main()中,如下所示,它可以工作。

int main(int argc,char ** argv){
  cout << "program started." << endl;
  TA<int> ta("local-ta");
  cout << "program stopped." << endl;
  return 0;
}

/////////////////////
//  OUTPUT:
constructor is called:static-tb
program started.
constructor is called:local-ta
static-tb
program stopped.
destructor is called:local-ta
destructor is called:static-tb
// end of output

In the first scenario, your program crashed before main started. 在第一个场景中,您的程序在主要启动之前崩溃了。 It crashes inside the constructor of ta because it accesses tb which was not yet constructed. 它在ta的构造函数内崩溃,因为它访问尚未构造的tb This is a form of the Static Initialization Order Fiasco 这是静态初始化订单Fiasco的一种形式

The second scenario was successful because ta is inside main , and that guaranteed that tb which is non-local, was constructed before ta . 第二种情况是成功的,因为tamain ,并且保证非ta tb是在ta之前构造的。

The question is, why in the first scenario, ta was constructed before tb even though tb and ta were defined in the same compilation unit, with tb defined before ta ? 问题是,为什么在第一种情况下, ta是在tb之前构建的,即使tbta在同一个编译单元中定义, tbta之前定义?

Knowing that tb is a template static data member, this quote from cppreference applies: 知道tb是模板静态数据成员, cppreference的引用适用:

Dynamic initialization 动态初始化

After all static initialization is completed, dynamic initialization of non-local variables occurs in the following situations: 完成所有静态初始化后,在以下情况下会发生非局部变量的动态初始化:

1) Unordered dynamic initialization, which applies only to (static/thread-local) variable templates and (since C++11) class template static data members that aren't explicitly specialized. 1)无序动态初始化,仅适用于(静态/线程局部)变量模板和(自C ++ 11以来)未明确专门化的类模板静态数据成员 Initialization of such static variables is indeterminately sequenced with respect to all other dynamic initialization except if the program starts a thread before a variable is initialized, in which case its initialization is unsequenced (since C++17). 这种静态变量的初始化对于所有其他动态初始化是不确定的顺序,除非程序在变量初始化之前启动一个线程,在这种情况下它的初始化是未排序的(因为C ++ 17)。 Initialization of such thread-local variables is unsequenced with respect to all other dynamic initialization. 对于所有其他动态初始化,此类线程局部变量的初始化未被排序。

So there's no sequence guaranteed here! 所以这里没有保证顺序! Since ta is a static variable with an explicit template specialization, the compiler was allowed to initialize it before tb . 由于ta是具有显式模板特化的静态变量,因此允许编译器在tb之前初始化它。

Another quote from the same page says: 同一页的另一个引用说:

Early dynamic initialization 早期动态初始化

The compilers are allowed to initialize dynamically-initialized variables as part of static initialization (essentially, at compile time), if the following conditions are both true: 如果以下条件都为真,则允许编译器初始化动态初始化变量作为静态初始化的一部分(基本上,在编译时):

1) the dynamic version of the initialization does not change the value of any other object of namespace scope prior to its initialization 1)初始化的动态版本在初始化之前不会更改命名空间作用域的任何其他对象的值

2) the static version of the initialization produces the same value in the initialized variable as would be produced by the dynamic initialization if all variables not required to be initialized statically were initialized dynamically. 2)初始化的静态版本在初始化变量中产生与动态初始化产生的值相同的值,如果所有不需要静态初始化的变量都是动态初始化的。 Because of the rule above, if initialization of some object o1 refers to an namespace-scope object o2, which potentially requires dynamic initialization, but is defined later in the same translation unit, it is unspecified whether the value of o2 used will be the value of the fully initialized o2 (because the compiler promoted initialization of o2 to compile time) or will be the value of o2 merely zero-initialized. 由于上面的规则,如果某个对象o1的初始化引用了命名空间范围对象o2,这可能需要动态初始化,但稍后在同一个转换单元中定义,则未指定所使用的o2的值是否为值完全初始化的o2(因为编译器将o2的初始化提升为编译时)或者o2的值仅为零初始化。

The compiler has decided according to these rules to promote the initialization of ta before tb . 编译器根据这些规则决定在tb之前促进ta的初始化。 Whether it was promoted to static initialization is not clear, but in any case, it seems pretty clear that the sequence of initialization is not guaranteed when it comes to variable templates and static templates members, due to the first quote and the promotion rules of the second quote. 它是否被提升为静态初始化并不清楚,但无论如何,由于第一个引用和第一个引用的推广规则,当涉及到变量模板和静态模板成员时,似乎很清楚初始化序列不能得到保证。第二个报价。

Solution

To ensure that tb is initialized before it is used, the simplest is to put it inside a wrapper function. 为确保在使用tb之前初始化tb ,最简单的方法是将其放在包装函数中。 I think that this should be somehow a rule of thumb when dealing with static templates members: 我认为在处理静态模板成员时,这应该是某种经验法则:

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM