C ++模板:全局对象中的静态成员未初始化 [英] C++ template: The static member in a global object is not initialized

查看:172
本文介绍了C ++模板:全局对象中的静态成员未初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一段简单的C ++代码,其中我通过专门化模板定义了模板和全局对象。对象构造函数访问专用模板中的静态成员。但事实证明,静态成员当时尚未初始化。但是对于本地对象(在函数主体中定义),它可以工作。我很困惑...

I have a piece of simple C++ code, in which I defined a template and a global object by specializing the template. 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...

我的c ++编译器是:g ++(Ubuntu 5.4.0-6ubuntu1〜16.04.4)5.4.0 20160609

My c++ compiler is: 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.

如果我将ta的定义放在main()中,如下所示,就可以了。

If I put the definition of ta in main() like the following, it works.

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


推荐答案

在第一种情况下,您的程序在main启动之前崩溃了。它在 ta 的构造函数内部崩溃,因为它访问了尚未构造的 tb 。这是静态初始化命令惨败的一种形式

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. This is a form of the Static Initialization Order Fiasco

第二种情况是成功的,因为 ta 位于 main 内部,这保证了非$本地的 tb 是在<$ c $之前构建的c> ta 。

The second scenario was successful because ta is inside main, and that guaranteed that tb which is non-local, was constructed before ta.

问题是,为什么在第一种情况下 ta 即使定义了 tb ta ,它也是在 tb 之前构建的在同一编译单元中,在 ta 之前定义了 tb

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?

知道 tb 是模板静态数据成员,来自cppreference的引用适用:

Knowing that tb is a template static data member, this quote from cppreference applies:


动态初始化

在完成所有静态初始化之后,在以下
情况下会发生
非局部变量的动态初始化:

After all static initialization is completed, dynamic initialization of non-local variables occurs in the following situations:

1)无序动态初始化,仅适用于未明确专用的(静态/线程本地)变量模板和(自C ++ 11起)类模板静态数据成员相对于所有其他动态初始化,不确定地初始化此类静态变量的顺序,除非程序在变量初始化之前启动了线程,在这种情况下,其初始化是未排序的(自C ++ 17起)。相对于所有其他动态初始化,此类线程局部变量的初始化没有顺序。

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. 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). Initialization of such thread-local variables is unsequenced with respect to all other dynamic initialization.

因此,这里没有保证的顺序!由于 ta 是具有 explicit 模板特性的静态变量,因此允许编译器在 tb

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.

同一页上的另一条引文说:

Another quote from the same page says:


早期动态初始化

如果满足以下两个条件,则允许编译器在静态初始化(本质上是在编译时)中初始化动态初始化的变量: :

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)动态版本的初始化不会在命名空间范围初始化之前更改任何其他对象的值

1) the dynamic version of the initialization does not change the value of any other object of namespace scope prior to its initialization

2)如果所有不需要静态初始化的变量都被动态初始化,则静态版本的初始化在初始化变量中产生的值与动态初始化产生的值相同。由于上述规则,如果某些对象o1的初始化引用了可能需要动态初始化的命名空间范围对象o2,但稍后在同一转换单元中对其进行了定义,则不确定使用的o2的值是否为该值完全初始化的o2的整数(因为编译器将o2的初始化提升为编译时间),或者将o2的值仅初始化为零。

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. 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.

编译器已根据这些规则决定在 tb 之前促进 ta 的初始化。目前尚不清楚是否将其升级为静态初始化,但是无论如何,由于变量的模板和静态模板成员的首引号和升级规则,在任何情况下似乎都无法保证初始化的顺序。

The compiler has decided according to these rules to promote the initialization of ta before tb. 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.

解决方案

确保 tb 在使用前已初始化,最简单的方法是将其放入包装函数中。我认为在处理静态模板成员时,这应该是一条经验法则。

To ensure that tb is initialized before it is used, the simplest is to put it inside a wrapper function. 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;
}

这篇关于C ++模板:全局对象中的静态成员未初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆