2016-10-24 38 views
16

在關於另一個問題的討論,我給出一個例子,其中的標識符顯然聯動影響了其在一個常量表達式可用性:爲什麼外部連接變量可用作常量表達式?

extern char const a[] = "Alpha"; 
char constexpr b[] = "Beta"; 
char const g[] = "Gamma"; 

template <const char *> void foo() {} 

auto main() 
    -> int 
{ 
    foo<a>();  // Compiles 
    foo<b>();  // Compiles 
    foo<g>();  // Doesn't compile 
} 

從最後(具有GCC)是誤差:

test.cc: In function 'int main()': 
test.cc:12:13: error: the value of 'g' is not usable in a constant expression 
     foo<g>();  // Doesn't compile 
      ^
test.cc:3:16: note: 'g' was not declared 'constexpr' 
    char const g[] = "Gamma"; 
       ^

我可能在前面的討論中錯過了這個例子的意義,因爲我相信它不可能只是連接區別foo<a>foo<g> - 但是,我已經開始懷疑這個位置。

  1. 難道真的聯動,或者是通過extern給予一些其他的屬性,允許foo<a>()
  2. 什麼是允許foo<a>()但不foo<g>()的理由?特別地,如果它是由連鎖確定,爲什麼要內部鏈接導致變量不被可用作常量表達式時相同的變量聲明extern是可用?
  3. 有人建議符號的問題是可見的(或不)的連接器與此有關。對我來說,即使添加了staticfoo<b>變體仍然被允許,這一事實似乎反駁了這一點 - 或者我誤會了嗎?
  4. foo<b>()foo<g>()之間的差被充分地other questions覆蓋,我想)。
+1

鏗鏘愉快地接受所有三個。 –

+0

@ T.C。以及它所做的......該死的。任何想法哪個編譯器是正確的? – davmac

+0

GCC 6也接受'-std = C++ 1z'。 –

回答

6

GCC bug。

N3337(這是C++ 11 +社論修復)[temp.arg.nontype]/2具有這是直接在點的例子:

template<class T, const char* p> class X { 
    /* ... */ 
}; 
X<int, "Studebaker"> x1; // error: string literal as template-argument 

const char p[] = "Vivisectionist"; 
X<int,p> x2; // OK 

在C++ 03參考/指針模板參數僅限於具有外部鏈接的事物,但在C++ 11中刪除了該限制。

在C++ 17中放寬了引用規則/指針模板參數以允許所有常量表達式,因此GCC接受-std=c++1z示例的原因可能是它在該模式下通過不同的代碼路徑。

+0

起首。十分有趣。 +1 – skypjack

3

這是一個奇怪的巧合。我剛剛在昨晚在C++ Templates上看到這個消息。當使用指針作爲模板非類型參數,它是包含在而不是值指向的由指針是取代的模板參數恆定的指針的地址。因此,地址在編譯時必須是可知的,並且在所有編譯單元中都是唯一的,以避免ODR違例。對於constexprextern變量,這是正確的,但不是那些具有文件或全局鏈接的變量。這是一個例子。

static char const foo[] = "Hello"; 
char const bar[] = "Hello"; 
constexpr char const baz[] = "Hello"; 
extern char const qux[] = "Hello"; 

template <char const*> 
struct my_struct{}; 

int main() { 
    my_struct<foo> f;  // ERROR: Address is unique, but not known until runtime 
    my_struct<bar> b;  // ERROR: Address may or may not be unique (ODR violation) and not known until runtime 
    my_struct<baz> bz;  // OK: constexpr 
    my_struct<qux> q;  // OK: extern 
} 
+0

這是不正確的,因爲C++ 11刪除了鏈接限制。 –

+1

在所有四個示例中,鏈接器都決定變量的地址。編譯器從不知道其他編譯單元將被混合在一起來創建最終的可執行文件。 –

+0

爲什麼地址或'bar'可能不是唯一的? –