簡體   English   中英

C ++模板:未初始化全局對象中的靜態成員

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

我有一段簡單的C ++代碼,其中我通過專門化模板來定義模板和全局對象。 對象構造函數訪問專用模板中的靜態成員。 但事實證明靜態成員在那時沒有被初始化。 但對於本地對象(在函數體中定義),它可以工作。 我糊塗了...

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

如果我把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

在第一個場景中,您的程序在主要啟動之前崩潰了。 它在ta的構造函數內崩潰,因為它訪問尚未構造的tb 這是靜態初始化訂單Fiasco的一種形式

第二種情況是成功的,因為tamain ,並且保證非ta tb是在ta之前構造的。

問題是,為什么在第一種情況下, ta是在tb之前構建的,即使tbta在同一個編譯單元中定義, tbta之前定義?

知道tb是模板靜態數據成員, cppreference的引用適用:

動態初始化

完成所有靜態初始化后,在以下情況下會發生非局部變量的動態初始化:

1)無序動態初始化,僅適用於(靜態/線程局部)變量模板和(自C ++ 11以來)未明確專門化的類模板靜態數據成員 這種靜態變量的初始化對於所有其他動態初始化是不確定的順序,除非程序在變量初始化之前啟動一個線程,在這種情況下它的初始化是未排序的(因為C ++ 17)。 對於所有其他動態初始化,此類線程局部變量的初始化未被排序。

所以這里沒有保證順序! 由於ta是具有顯式模板特化的靜態變量,因此允許編譯器在tb之前初始化它。

同一頁的另一個引用說:

早期動態初始化

如果以下條件都為真,則允許編譯器初始化動態初始化變量作為靜態初始化的一部分(基本上,在編譯時):

1)初始化的動態版本在初始化之前不會更改命名空間作用域的任何其他對象的值

2)初始化的靜態版本在初始化變量中產生與動態初始化產生的值相同的值,如果所有不需要靜態初始化的變量都是動態初始化的。 由於上面的規則,如果某個對象o1的初始化引用了命名空間范圍對象o2,這可能需要動態初始化,但稍后在同一個轉換單元中定義,則未指定所使用的o2的值是否為值完全初始化的o2(因為編譯器將o2的初始化提升為編譯時)或者o2的值僅為零初始化。

編譯器根據這些規則決定在tb之前促進ta的初始化。 它是否被提升為靜態初始化並不清楚,但無論如何,由於第一個引用和第一個引用的推廣規則,當涉及到變量模板和靜態模板成員時,似乎很清楚初始化序列不能得到保證。第二個報價。

為確保在使用tb之前初始化tb ,最簡單的方法是將其放在包裝函數中。 我認為在處理靜態模板成員時,這應該是某種經驗法則:

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