2013-06-27 18 views
6

我有以下工作代碼:爲什麼我不能做一流初始化`常量常量的std :: string`靜態成員

#include <string> 
#include <iostream> 

class A { 
public: 
    const std::string test = "42"; 
    //static const std::string test = "42"; // fails 
}; 

int main(void){ 
    A a; 
    std::cout << a.test << '\n'; 
} 

有一個很好的理由,不可能使測試一個static const?我在C++ 11之前就明白它受到標準的約束。我認爲C++ 11引入了類內初始化,使它更友好一些。我也沒有這樣的語義可用於整型,因爲相當長的一段時間。

當然,它的工作原理與外的類的初始化在const std::string A::test = "42";

我想,如果你可以把它非靜態的,那麼問題就出在這兩個的一種形式。初始化它外的類範圍(通常const s的對象的實例化期間創建的)。但是,如果你正在創建一個獨立於任何其他類的成員的對象,我認爲這不是問題。第二個是對靜態成員有多個定義。例如。如果它被包含在多個文件.cpp,登陸成幾個對象的文件,然後連接這些物體一起(例如爲一個可執行文件)時,接頭將具有煩惱,因爲它們將包含相同的符號的副本。據我瞭解,這是完全相等的情況時,那些提供了課外權下的標題的類的聲明,幷包括一個以上的地方這個共同的頭。我記得,這導致鏈接器錯誤。

但是,現在將處理這個問題的責任轉移到用戶/程序員身上。如果想要有他們需要提供外的類的定義,它編譯成一個獨立的目標文件,然後將所有其他對象鏈接到這一個static庫,因此具有二元的定義只有一個副本符號。

我在Do we still need to separately define static members, even if they are initialised inside the class definition?Why can't I initialize non-const static member or static array in class?閱讀答案。

我還是想知道:

  1. 難道僅僅是一個標準的東西,還是有更深層次的推理背後?
  2. 這可以用constexpr和用戶定義的 文字機制來解決。 clang和g ++都表示變量不能有非文字類型。也許我可以做一個。 (也許由於某種原因,它也是一個壞主意)
  3. 鏈接器是否真的有這麼大的問題只包含 符號的一個副本?既然是static const都應該是二進制精確 一成不變的副本。

如果我失蹤或誤解某事,Plese也會發表評論。那種

+0

說到靜態,只能在聲明點初始化'const'整型和枚舉。 – juanchopanza

+0

@juanchopanza我知道。我認爲在C++ 11中它可以被克服。 – luk32

+0

我認爲你在3中指出了其中的一個問題。是的,這是可行的,但它需要這種情況下是特殊情況,否則鏈接器不會「喜歡」多個定義。 – ondrejdee

回答

2

你的問題有兩個部分。標準說什麼?爲什麼這樣呢?

對於類型爲const std::string的靜態成員,它需要在類說明符之外定義,並且在其中一個翻譯單元中有一個定義。這是一個定義規則的一部分,在C++標準的第3條中有詳細說明。

但是爲什麼?

問題是具有靜態存儲持續時間的對象在最終的程序映像中需要唯一的靜態存儲,所以它需要從一個特定的翻譯單元進行鏈接。類說明符在一個翻譯單元中沒有家,它只是定義了類型(在使用它的所有翻譯單元中需要相同定義)。

常量積分不需要存儲的原因是編譯器將其用作常量表達式並在使用點處內聯。它永遠不會影響程序映像。

但是,具有靜態存儲持續時間的複雜類型(如std::string)需要存儲,即使它們是const。這是因爲它們可能需要被動態初始化(在main入口之前調用它們的構造函數)。

您可能會爭辯說,編譯器應該在每個使用它們的翻譯單元中存儲靜態存儲持續時間的對象信息,然後鏈接程序應該將這些定義在鏈接時合併到程序圖像中的一個對象中。我爲什麼不這樣做的猜測是,它需要鏈接器提供太多的信息。

+0

「常量積分不需要存儲的原因在於它被編譯器用作常量表達式並在使用點內聯,它永遠不會影響程序映像。」除了我可以編寫需要存儲的'&const_int_member'。而我永遠不會參考的靜態常量int不需要存儲。最後,模板需要與鏈接器完全相同的「太多智能」。所以,據我所知,整個論點在每個細節上都是無稽之談。 (並不是說你一個人在做......) – Nemo

+0

@Nemo:執行間接尋址('&const_int_member')使它變得可用,因此9.4.2p4中提到的要求發揮作用,即「如果在程序中使用odr-use(3.2)並且命名空間範圍定義不應包含初始值設定項, 仍應在名稱空間範圍內定義 。 (即它現在需要一個定義)至於模板,他們有特殊的_instantiation units_在翻譯階段8中使用特殊規則。 –

+0

好的,但只要你使它「odr-used」,它有所有相同的問題任何用戶定義的類型。所以理論依然分崩離析。沒有理由不讓任何類型在頭文件中初始化,即使您仍然需要命名空間範圍內的某個單個定義。 「const int」和「const whatever」之間的這種區別在你思考時真的沒有任何意義,據我所知... – Nemo

相關問題