2015-09-27 158 views
8

我對staticconst成員的課程初始化有點困惑。例如,在下面的代碼:類中靜態常量ODR

#include <iostream> 

struct Foo 
{ 
    const static int n = 42; 
}; 

// const int Foo::n; // No ODR 

void f(const int& param) 
{ 
    std::cout << param << std::endl; 
} 

int g(const int& param) 
{ 
    return param; 
} 

template<int N> 
void h() 
{ 
    std::cout << N << std::endl; 
} 

int main() 
{ 
    // f(Foo::n); // linker error, both g++/clang++ 
    std::cout << g(Foo::n) << std::endl; // OK in g++ only with -O(1,2 or 3) flag, why?! 
    h<Foo::n>(); // this should be fine 
} 

Live example

我不限定Foo::n(線被註釋)。所以,我預計電話f(Foo::n)在鏈接時失敗,事實上它確實如此。但是,每當我使用優化標誌(如-O1/2/3)時,以下行std::cout << g(Foo::n) << std::endl;僅由gcc編譯和鏈接(clang仍會發出鏈接器錯誤)。

  1. 爲什麼海灣合作委員會(試圖用gcc5.2.0和gcc 4.9.3)編譯和鏈接代碼時優化是否已經開啓?
  2. 我正確地說,在課堂上靜態const成員的唯一用法是在常量表達式,如模板參數如h<Foo::n>調用,在這種情況下代碼應鏈接?
+2

[odr違規不需要診斷](http://stackoverflow.com/a/28446388/1708801)。 –

+0

這是專門針對C++ 11的問題嗎? odr引號[在C++ 11和C++ 14之間改變了一點](http://stackoverflow.com/a/28846608/1708801),雖然我不認爲它在這種情況下確實很重要,但在某些情況下它確實。 –

+0

@ShafikYaghmour我用'-std = C++ 11'編譯,但問題本身不一定只用C++ 11(或C++ 14)。 O也觀察到與'-std = C++ 98'相同的行爲。 – vsoftco

回答

4

ODR違規不需要C++標準草案部分草案3.2 [basic.def。ODR(重點礦山前進):

每個程序應包括每一個非內嵌 函數或變量,它是ODR使用的在該程序中的恰好一個定義; 無診斷 需要

因此,不同優化級別的不一致行爲是完全一致的行爲。

非正式的變量是odr-used如果:

其地址被佔用,或引用綁定到它,並且如果一個函數調用它是由一個功能是ODR使用或它的地址取。如果一個對象或函數被使用了,它的定義必須存在於程序中的某個地方;違反該規定的是鏈接時間錯誤。

因此,fg將odr用途和需要定義。

上ODR使用相關C++ 14引用將是從節[basic.def.odr]

出現一個變量x,其名稱爲潛在評估表達EX是odr-由ex使用除非應用 左值到右值轉換(4.1)到x產生不調用任何不平凡 函數的常量表達式(5.19),如果x是對象,則ex爲的元素表達式e的潛在結果集 其中左值到右值轉換(4.1)被施加到E,或e爲丟棄值表達式 [...]

措辭在C++ 11是相似的,從C++ 11到C++ 14的變化反映在defect report 712

在C++ 11之前它是a bit more complicated but in principle the same for this case

2

根本沒有定義。 GCC 4.9.2不會編譯和鏈接任何標誌。

注意,即:

const static int n = 42; 

聲明初始,但不是定義

+0

我知道這不是定義,但正如您從現場示例中所看到的,代碼已編譯並鏈接。 – vsoftco

+0

我嘗試了GCC 4.9.2,但沒有。 – Nevermore

+0

這很奇怪。我沒有gcc4.9.2,但在OS X上有macintosh的4.9.3,它編譯和鏈接。 – vsoftco

4

我想,編譯器優化過程中執行以下操作:

  • const static int n到處內聯。沒有內存分配給變量n,對其的引用變爲無效。函數f()需要對n的引用,因此程序未編譯。

  • 功能g簡短。它被有效地內聯和優化。優化後,將函數g不需要n的引用,它只是返回恆定值42.

的解決方案是定義類以外的變量:

struct Foo 
{ 
    const static int n; 
}; 

const int Foo::n = 42; 
+0

這是有道理的。 – vsoftco

+0

替代解決方案是使它成爲constexpr或枚舉值。一個'constexpr'可以說比較好,現在編譯器一般都支持它,因爲它的源代碼較少,並且它在頭文件中很好地工作。 –

4

形式上,ODR違規是未定義的行爲,所以編譯器可能會表現出它喜歡的任何行爲。這就是爲什麼行爲隨着優化級別和編譯器而變化的原因 - 編譯器沒有義務維護特定的行爲。