2013-03-19 54 views
12

假設我們有下面的代碼:字符串文字模板 - 不同的行爲編譯器的

template <typename T> 
void foo(const T&); 

int main() 
{ 
    foo("str"); 
} 

Demonstration

GCC 4.7.2,鐺3.2,ICC 13.0.1

未定義引用'void foo < char [4]>(char const(& )[4])」

MSVC-11.0

解析外部符號「空隙__cdecl FOO < 字符常量 [4]>(炭 常量(&)[4]) 「(?? $ foo的@ $$ BY03 $$ CBD @@ YAXAAY03 $$ CBD @ Z)

通知char[4]在第二O的第一輸出和char const[4]本安輸出。

爲什麼?誰是對的?你能引用標準嗎?

+7

兩者都在抱怨沒有'foo'定義.... – UmNyobe 2013-03-19 13:57:34

+0

@UmNyobe是的,我知道,它只需要打印類型 – FrozenHeart 2013-03-19 13:59:20

+0

@UmNyobe我的意思是什麼類型應該在這種情況下 - char [4]或char const [4]? – FrozenHeart 2013-03-19 14:00:08

回答

1

GCC是正確的;這const在VS的模板參數列表不應該存在:進行這種替代

[C++11: 14.8.2/3]:後,在8.3.5中描述的函數參數類型調整執行。 [示例:參數類型「void()(const int, int[5])」變成「void(*)(int,int*)」- 結束示例][注意:函數參數聲明中的頂級限定符不影響函數 類型,但仍影響函數中函數參數變量的類型。 末端音符][實施例:

template <class T> void f(T t); 
template <class X> void g(const X x); 
template <class Z> void h(Z, Z*); 

int main() { 
    // #1: function type is f(int), t is non const 
    f<int>(1); 

    // #2: function type is f(int), t is const 
    f<const int>(1); 

    // #3: function type is g(int), x is const 
    g<int>(1); 

    // #4: function type is g(int), x is const 
    g<const int>(1); 

    // #5: function type is h(int, const int*) 
    h<const int>(1,0); 
} 

末端示例]

(實施例4是相關的。)

[C++11: 14.8.2/5]:將所得使用替代和調整的函數類型作爲f的類型模板論證扣除的聯合模板。[..]

而且可能相關:

從一個函數調用推導模板參數
[C++11: 14.8.2.1/2]:如果P是不是引用類型:

  • 如果A是數組類型,則使用由數組到指針標準轉換(4.2)生成的指針類型來代替A用於類型扣除;否則,
  • 如果A是函數類型,則使用由函數指針標準轉換(4.3)生成的指針類型來代替類型推導的A;否則,
  • 如果AA「S型的CV-限定的類型,頂層CV-預選賽類型扣除被忽略
+1

我們都知道這一點,但它是如何適用於有關案件? – 2013-03-19 14:37:02

+0

@JamesKanze:'const'不是函數類型的一部分,函數類型用於模板參數推導。因此,在這種情況下'T'不應該是'const',但在OP的例子中MSVC已經這樣做了。 – 2013-03-19 14:37:42

+0

我確實認爲我可能會錯過幾個步驟。 :(想到我會把它扔到那裏,儘管如此 – 2013-03-19 14:40:27

5

GCC是正確的。

讓我們從一個稍微簡單的例子開始,後來證明原來的例子遵循相同的模式:

template<typename T> 
void bar(T const&) 
{ 
    // Shall not fire 
    static_assert(std::is_same<T, int>::value, "Error!"); 
} 

int main() 
{ 
    int x = 0; 
    bar(x); // 1 - Assertion won't fire 

    int const y = 0; 
    bar(y); // 2 - Assertion won't fire 
} 

這裏發生了什麼?首先,根據§14.8.2.1/3:

[... ...]如果P是一個參考類型,P引用的類型用於類型推導。 [...]

這意味着該類型的扣將嘗試匹配T const針對int(在情況1)和針對int const(在情況2)。在第二種情況下,用int代替T將產生完美匹配,所以很容易;在第一種情況下,我們有const開始有一個完美的匹配。但是,這是§14.8.2.1/4的用武之地:

[...] 如果原來的P是引用類型,推導A(由參考簡稱,即,類型) 可以是 更CV-合格比轉化A. [...]

在這裏,替換爲intT給我們帶來了推導int const,這是更CV-合格比int(的類型參數x)。但由於上述第14.8.2.1/4條,這是可以接受的,所以即使在這種情況下,T也被推斷爲int

現在讓我們來解決您的原來的例子(只是略作調整,但我們會得到原始版本最終):

template<typename T> 
void bar(T const&) 
{ 
    // Does not fire in GCC, fires in VC11. Who's right? 
    static_assert(std::is_same<T, char[4]>::value, "Error!"); 
} 

int main() 
{ 
    char x[] = "foo"; 
    bar(x); 

    char const y[] = "foo"; 
    bar(y); 
} 

除此之外,我換成intchar []的事實,這是例子,我第一個例子在結構上是相同的。爲了說明爲什麼這等價成立,考慮下面的斷言(不上觸發任何編譯器,如預期):

// Does not fire 
static_assert(
    std::is_same< 
     std::add_const<char [4]>::type, 
     char const[4] 
    >::value, "Error"); 

的C++標準11本任務中的行爲3.9.3款/ 2 :

應用於數組類型的任何cv限定符都會影響數組元素類型,而不是數組類型(8.3.4)。

段落8.3.4/1也規定:

[...]任何類型的形式是「NT的CV-限定符-SEQ陣列」的被調整到「N CV的陣列 -qualifier-seq T「,對於」T的未知範圍數組「也是類似的。可選的屬性說明符seq 屬於數組。 [實施例:

typedef int A[5], AA[2][3]; 
typedef const A CA; // type is 「array of 5 const int」 
typedef const AA CAA; // type is 「array of 2 array of 3 const int」 

末端示例] [注:所謂「N- CV-限定符-SEQ的T數組」具有CV-限定類型;見3.9.3。 - 注意]

由於現在很清楚這兩個示例呈現相同的模式,因此應用相同的邏輯是有意義的。這將引導我們走完全相同的推理路徑。

在執行類型扣除時,T const在第一種情況下與char[4]匹配,在第二種情況下與char const[4]匹配。

在第二種情況下,T = char[4]產生完美匹配,因爲T const在替換後變爲char const[4]。在第一種情況下,推導出的A再次比原始的A更符合CV條件,其中用char[4]代替T得到char const[4]。但是再一次,14.8.2.1/4允許,所以T應該推導爲char[4]

最後回到你原來的例子。由於字符串文字"str"還具有類型char const[4]T應當推斷爲char [4],這意味着GCC是右

template<typename T> 
void foo(T const&) 
{ 
    // Shall not fire 
    static_assert(std::is_same<T, char[4]>::value, "Error!"); 
} 

int main() 
{ 
    foo("str"); // Shall not trigger the assertion 
} 
+0

但爲什麼'std :: add_const :: type'與char const [4]'相同?這似乎並不明顯 - 第一個是4個字符的常量數組,而第二個是4個常量字符的數組。 – interjay 2013-03-19 21:53:54

+0

@interjay:請參見C++ 11標準的3.9.3/2:「[...]應用於數組類型的任何cv限定符影響數組元素類型,而不是數組類型(8.3.4)。」 – 2013-03-19 23:25:18

+0

@interjay:我在最後一次更新中對此進行了擴展 – 2013-03-19 23:29:56