亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

C ++靜態成員初始化(內部模板有趣)

C ++靜態成員初始化(內部模板有趣)

C++
嗶嗶one 2019-12-15 14:08:42
對于靜態成員初始化,我使用嵌套的輔助結構,該結構適用于非模板化類。但是,如果封閉類是由模板參數化的,則如果未在主代碼中訪問輔助對象,則不會實例化嵌套的初始化類。為了說明,一個簡化的示例(在我的情況下,我需要初始化一個向量)。#include <string>#include <iostream>struct A{    struct InitHelper    {        InitHelper()        {            A::mA = "Hello, I'm A.";        }    };    static std::string mA;    static InitHelper mInit;    static const std::string& getA(){ return mA; }};std::string A::mA;A::InitHelper A::mInit;template<class T>struct B{    struct InitHelper    {        InitHelper()        {            B<T>::mB = "Hello, I'm B."; // [3]        }    };    static std::string mB;    static InitHelper mInit;    static const std::string& getB() { return mB; }    static InitHelper& getHelper(){ return mInit; }};template<class T>std::string B<T>::mB; //[4]template<class T>typename B<T>::InitHelper B<T>::mInit;int main(int argc, char* argv[]){    std::cout << "A = " << A::getA() << std::endl;//    std::cout << "B = " << B<int>::getB() << std::endl; // [1]//    B<int>::getHelper();    // [2]}使用g ++ 4.4.1:[1]和[2]評論:A =你好,我是A。按預期工作[1]條未評論:A =你好,我是A。B = 我希望InitHelper初始化mB[1]和[2]未評論:A =你好,我是A。B =你好,我是B。按預期工作[1]評論,[2]未評論:Segfault在[3]的靜態初始化階段因此,我的問題是:這是編譯器錯誤還是監視器和椅子之間的錯誤?如果是后者,是否存在一種優雅的解決方案(即,無需顯式調用靜態初始化方法)?更新I:這似乎是理想的行為(在ISO / IEC C ++ 2003標準14.7.1中定義):除非已明確實例化或顯式實例化了類模板或成員模板的成員,否則在需要成員定義存在的上下文中引用專門化時,將隱式實例化成員的專門化。特別是,除非靜態數據成員本身以要求靜態數據成員的定義存在的方式使用,否則不會發生靜態數據成員的初始化(以及任何相關的副作用)。
查看完整描述

3 回答

?
子衿沉夜

TA貢獻1828條經驗 獲得超3個贊

前段時間在usenet上對此進行了討論,而當時我正試圖回答關于stackoverflow的另一個問題:靜態數據成員的實例化點。我認為減少測試用例,并單獨考慮每種情況是值得的,所以讓我們首先更一般地看一下它:


struct C { C(int n) { printf("%d\n", n); } };


template<int N>

struct A {

  static C c;

}; 


template<int N>

C A<N>::c(N); 


A<1> a; // implicit instantiation of A<1> and 2

A<2> b;

您已定義了靜態數據成員模板。由于以下原因,此操作尚未創建任何數據成員14.7.1:


“ ...尤其是,除非以某種方式要求使用靜態數據成員的定義才能使用靜態數據成員,否則不會發生靜態數據成員的初始化(以及任何相關的副作用)。”


根據定義該詞的一種定義規則(在處3.2/2),當“使用”某個實體時,需要定義某種事物(=實體)。特別是,如果所有引用均來自未實例化的模板,模板的成員或sizeof表達式或不“使用”實體的類似事物(因為它們可能沒有對實體進行評估,或者它們還不作為函數存在) / member函數本身),則不會實例化此類靜態數據成員。


隱式實例化通過14.7.1/7實例化靜態數據成員的聲明-也就是說,它將實例化處理該聲明所需的任何模板。但是,它不會實例化定義-也就是說,不會實例化初始化程序,并且不會隱式定義該靜態數據成員類型的構造函數(標記為已使用)。


這一切都意味著,以上代碼將不會輸出任何內容。現在讓我們引起靜態數據成員的隱式實例化。


int main() { 

  A<1>::c; // reference them

  A<2>::c; 

}

這將導致兩個靜態數據成員存在,但問題是-初始化的順序如何?簡單閱讀一下,可能會認為這3.6.2/1適用,(我強調):


“具有在同一翻譯單元中的名稱空間范圍內定義并動態初始化的具有靜態存儲持續時間的對象,應按照其定義在翻譯單元中出現的順序進行初始化。”


現在,如usenet帖子中所述,并在此缺陷報告中進行了說明,這些靜態數據成員未在轉換單元中定義,但是在實例化單元中實例化,如以下所述2.1/1:


檢查每個翻譯的翻譯單元,以產生所需實例的列表。[注意:這可能包括已明確請求的實例化(14.7.2)。]找到了所需模板的定義。由實現定義,是否要求包含這些定義的翻譯單元的源是否可用。[注意:一個實現可以將足夠的信息編碼到翻譯的翻譯單元中,以確保此處不需要源。]執行所有必需的實例化以生成實例化單元。[注意:它們類似于翻譯后的翻譯單元,但不包含對未實例化模板的引用,也沒有模板定義。]如果任何實例化失敗,則程序格式錯誤。


這樣的成員的實例化點也并不重要,因為這樣的實例化點是實例化及其轉換單元之間的上下文鏈接-它定義了可見的聲明(如處所指定14.6.4.1,以及的每個點)實例化必須賦予實例化相同的含義,如3.2/5最后一個項目符號的一個定義規則中所指定)。


如果我們要進行有序的初始化,則必須進行安排,以免混淆實例化,而是使用顯式聲明-這是顯式專業化的領域,因為它們與普通聲明沒有真正的不同。實際上,C ++ 0x將其措辭更改3.6.2為以下內容:


具有靜態存儲持續時間的非本地對象的動態初始化是有序的或無序的。顯式專門化的類模板靜態數據成員的定義已進行了初始化。其他類模板靜態數據成員(即,隱式或顯式實例化的專長)具有無序初始化。


這對您的代碼意味著:


[1]并[2]評論:不存在對靜態數據成員的引用,因此B<int>不會實例化它們的定義(并且也不需要聲明,因為不需要實例化)。沒有副作用發生。

[1]uncommented:B<int>::getB()被使用,其本身使用B<int>::mB,這要求該靜態成員存在。字符串在main之前初始化(無論如何,在該語句之前,作為初始化非本地對象的一部分)。什么都沒有使用B<int>::mInit,所以它不會被實例化,因此B<int>::InitHelper也不會創建任何對象,這使得它的構造函數沒有被使用,這反過來將永遠不會給分配任何東西B<int>::mB:您只會輸出一個空字符串。

[1]并[2]取消注釋:本工作對你來說是運氣(或者相反:))。如上所述,不需要特定順序的初始化調用。它可能在VC ++上運行,在GCC上失敗,在clang上運行。我們不知道

[1]評論說,[2]取消注釋:同樣的問題-再次,這兩個靜態數據成員的使用:B<int>::mInit使用的B<int>::getHelper,和的實例B<int>::mInit被實例化會導致它的構造函數,將使用B<int>::mB-但你的編譯器,順序是在這個特殊的運行不同(未指定的行為不需要在不同的運行之間保持一致):首先進行初始化B<int>::mInit,該操作將在尚未構造的字符串對象上進行。



查看完整回答
反對 回復 2019-12-16
?
鳳凰求蠱

TA貢獻1825條經驗 獲得超4個贊

  • [1]未注釋的情況:可以。static InitHelper B<int>::mInit不存在。如果不使用模板類(struct)的成員,則不會編譯。

  • [1]和[2]未注釋的情況:可以。B<int>::getHelper()使用static InitHelper B<int>::mInitmInit存在。

  • [1]評論,[2]未評論:它在VS2008中對我有用。



查看完整回答
反對 回復 2019-12-16
  • 3 回答
  • 0 關注
  • 374 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號