2008-11-07 96 views
172

任何人都可以解釋爲什麼下面的代碼不能編譯?至少在g ++ 4.2.4上。對靜態類成員的未定義引用

更有趣的是,爲什麼它會在我將MEMBER投射到int時編譯?

#include <vector> 

class Foo { 
public: 
    static const int MEMBER = 1; 
}; 

int main(){ 
    vector<int> v; 
    v.push_back(Foo::MEMBER);  // undefined reference to `Foo::MEMBER' 
    v.push_back((int) Foo::MEMBER); // OK 
    return 0; 
} 
+0

我編輯了這個問題,讓代碼縮進四個空格而不是使用

。這意味着尖括號不會被解釋爲HTML。 – 2008-11-07 19:03:01

+0

歡呼聲:) 10個字符的最小規則有時很討厭;) – 2008-11-08 12:30:10

回答

176

您需要在某處(類定義之後)實際定義靜態成員。試試這個:

class Foo { /* ... */ }; 

const int Foo::MEMBER; 

int main() { /* ... */ } 

這應該擺脫未定義的參考。

+3

好點,內聯靜態常量整數初始化創建一個範圍的整數常量,你不能取地址,而向量需要一個參考參數。 – 2008-11-07 18:08:07

+9

此答案只解決問題的第一部分。第二部分更有意思:爲什麼添加一個NOP類型可以在不需要外部聲明的情況下工作? – nobar 2011-02-01 00:48:40

+27

我只花了很長時間弄清楚,如果類定義在頭文件中,那麼靜態變量的分配應該在實現文件中,而不是頭中。 – shanet 2012-07-14 03:06:28

69

問題來自於新C++特性的有趣衝突以及您正在嘗試做什麼。首先,讓我們來看看push_back簽名:

void push_back(const T&) 

該公司預計,到T類型的對象的引用。在舊的初始化系統下,存在這樣的成員。例如,下面的代碼編譯得很好:

#include <vector> 

class Foo { 
public: 
    static const int MEMBER; 
}; 

const int Foo::MEMBER = 1; 

int main(){ 
    std::vector<int> v; 
    v.push_back(Foo::MEMBER);  // undefined reference to `Foo::MEMBER' 
    v.push_back((int) Foo::MEMBER); // OK 
    return 0; 
} 

這是因爲有一個實際的對象存儲了該值。但是,如果您切換到指定靜態常量成員的新方法(如上所述),則Foo::MEMBER不再是對象。它是一個常數,有點類似於:

#define MEMBER 1 

但沒有預處理器宏(和類型安全)的麻煩。這意味着期待參考的矢量不能得到一個。

1

不知道爲什麼演員會工作,但是Foo :: MEMBER在第一次加載Foo之前​​不會被分配,並且由於您從未加載Foo :: MEMBER,所以從未分配Foo :: MEMBER。如果你在某處引用了Foo,它可能會起作用。

56

如果需要定義,C++標準需要定義靜態const成員。

定義是必需的,例如,如果使用的是地址。 push_back通過const引用獲取其參數,因此嚴格地說編譯器需要您的成員的地址,並且您需要在名稱空間中定義它。

當您明確地轉換常量時,您正在創建一個臨時參數,並且它是綁定到參考的臨時參數(在標準中的特殊規則下)。

這是一個非常有趣的案例,實際上我認爲值得提出一個問題,以便std被更改爲對於常量成員具有相同的行爲!

雖然,以一種奇怪的方式,這可以被視爲一元'+'運算符的合法使用。基本上unary +的結果是右值的結合爲const引用適用的右值等規則,我們不使用我們的靜態const成員地址:

v.push_back(+Foo::MEMBER); 
9

Aaa.h

class Aaa { 

protected: 

    static Aaa *defaultAaa; 

}; 

Aaa。CPP

// You must define an actual variable in your program for the static members of the classes 

static Aaa *Aaa::defaultAaa; 
0

關於第二個問題:push_ref需要引用作爲參數,並且你不能有一類/結構的靜態常量memeber參考。一旦你調用static_cast,就會創建一個臨時變量。並且可以傳遞這個對象的引用,一切正常。

或者至少我的同事誰解決這個如此說。

1

用C++ 11,上面將基本類型是可能的

class Foo { 
public: 
    static constexpr int MEMBER = 1; 
}; 

constexpr部分創建一個靜態表達,而不是靜態可變 - 和行爲就像一個極其簡單的內聯方法定義。不過,這種方法在模板類中使用C字符串constexprs證明有點不穩定。