2013-08-27 37 views
4

我正在使用類似以下代碼段的內容來進行一些初始化。我知道p<T>::i_的初始化是無序的。我認爲h是有序的,所以我應該能夠推斷它初始化的順序。鑑於p的標題包含在h的定義之前,是否保證p<T>::i_將在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_; 
+0

這不會編譯 - 'helper'沒有默認的構造函數。 –

+0

什麼構造函數被用來初始化'h'? –

+3

「我相信'h'是有序的」。相比有什麼? – jrok

回答

6

具有靜態存儲持續時間的對象的初始化順序在翻譯單元中是未定義的,並且在每個翻譯單元中是連續的。

在您的特殊情況下,事情更復雜,因爲具有靜態存儲的對象之一是模板類的靜態成員。這實際上意味着訪問成員p<T>::i_的每個翻譯單元都將創建符號,並添加適當的初始化代碼。之後,鏈接器將選擇其中一個實例並保留它。即使在您的翻譯單元中看起來像p<T>::i_被定義爲之前h,但您不知道鏈接器將保留哪個p<T>::i_實例,並且這可能是一個位於不同翻譯單元中的實例,因此順序不能保證。

一般來說,擁有全局對象是一個壞主意,我建議您嘗試重新設計沒有這些全局變量的程序。

+0

+1提及翻譯單元/鏈接器參與。我想知道模板中的靜態非模板變量是否在模板實例化時被初始化?或者,無論模板是否被實例化(似乎有點奇怪),它都被初始化了? – lapk

+0

重要的是,模板的隱式實例*不觸發靜態成員的初始化。當它們以需要定義的方式使用時,它們將被初始化。 – jrok

+0

@PetrBudnik:除非您明確實例化模板,否則所有成員都會根據需要進行實例化。如果程序不使用模板的靜態成員,那麼成員不會被編譯器實例化,除非它被使用。 –

5

全局或命名空間範圍內的對象在一個翻譯單元內從上到下構建。沒有定義在不同翻譯單元之間建立全局或名稱空間級別的順序。最合理的方式訂購翻譯單元之間的初始化它合適的訪問函數內包裹的物體,如:

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

但是請注意,這不是線程安全的C++ 03(如C++ 03沒有線程的概念)。它在C++ 11中是線程安全的。

0

不,不能保證。然而

你可以做的是:

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

... 
registry<T>().push_back(i); 
... 

更妙的是要避免做的事情是在啓動過程中太聰明瞭。

main開始之前或結束之後的調試是一個真正的噩夢(IMO甚至沒有在標準中覆蓋100%)。簡單的註冊也許可以,但是千萬不要做任何可能會失敗的事情。

多年來,我擺脫了這種顯式初始化/關閉的方式,從不回頭。

+0

調試動態初始化可能並不像您想象的那麼困難。有一個與平臺相關的符號可以打破這個標誌,將動態初始化的條目標記出來,然後您可以像任何函數一樣遍歷它。 –

+0

@ user1131467:我已經看到,調試工具在初始化和關機期間無法按預期工作。請注意,在初始化期間,甚至不清楚標準庫有多少已經被初始化並且可以被使用,並且在銷燬靜態持續時間實例期間已經關閉了多少標準庫。除此之外,例如在某些情況下,窗口在退出應用程序時只是默默吞下段錯誤... – 6502