2011-03-02 37 views
34

任何一個可以幫助我瞭解下面的代碼通參考數組中的C++

#include <iostream> 

void foo(const char * c) 
{ 
    std::cout << "const char *" << std::endl; 
} 

template <size_t N> 
void foo(const char (&t) [N]) 
{ 
    std::cout << "array ref" << std::endl; 
    std::cout << sizeof(t) << std::endl; 
} 

int main() 
{ 
    const char t[34] = {'1'}; 
    foo(t); 

    char d[34] = {'1'}; 
    foo(d); 
} 

輸出是

const char * 
array ref 
34 

爲什麼第一個foo的調用const char *版本?我怎樣才能讓它調用參考版本?

+0

msvc輸出爲'const char *,const char *' – Marlon 2011-03-02 21:14:22

+0

輸出如他所說,'const char *,array ref,34'with'gcc-4.3.4'(http:// ideone.com/ejyCS)。 – James 2011-03-02 21:18:06

+1

@ user511274 - 有趣的問題:) – 2011-03-02 21:19:51

回答

15

const char[N]轉換爲const char*被認爲是「完全匹配」(主要是爲了使文字更容易),並且在兩個完全匹配之間,非模板函數優先。

您可以使用enable_ifis_array強制它做你想做的。


一個混亂的辦法,迫使它可能是:

#include <iostream> 

template <typename T> 
void foo(const T* c) 
{ 
    std::cout << "const T*" << std::endl; 
} 

template <typename T, size_t N> 
void foo(const T (&t) [N]) 
{ 
    std::cout << "array ref" << std::endl; 
} 

int main() 
{ 
    const char t[34] = {'1'}; 
    foo(t); 

    char d[34] = {'1'}; 
    foo(d); 
} 

/* 
array ref 
array ref 
*/ 

我意識到,OP有char沒有一些通用T,但儘管如此這表明,這個問題在一個過載躺在是一個模板,而不是其他。

+1

你怎麼樣解釋第二個版本的'foo()'是第二次調用? – fouronnes 2011-03-02 21:27:00

+1

@otibom:'char [N]'的轉換沒有相同的規則。 'char [N]'到'const char *'不是完全匹配:它是可轉換的,但模板函數更容易匹配。 – 2011-03-02 21:33:18

+0

@otibom第一個版本不是const,但模板版本需要一個const數組指針。 – Lundin 2011-03-02 21:36:18

5

讓我們來看看這個沒有模板的修改示例。

void foo(const char * c) 
{ 
    std::cout << "const char *" << std::endl; 
} 

void foo(const char (&t) [34]) 
{ 
    std::cout << "const char (&) [34]" << std::endl; 
} 

int main() 
{ 
    const char t[34] = {'1'}; 
    foo(t); 
} 

我的編譯器說過載foo的調用是不明確的。這是因爲從陣列到指針的轉換被認爲是「精確」轉換序列,並不比用於重載分辨率的空轉換序列好(標準部分13.3.3.1.1)。

在原始代碼中,模板參數N可以被推斷爲34,但是然後在重載分辨率中考慮非模板foo(const char*)foo<34>(const char (&)[34])。由於轉換規則既不比另一個更好,非模板函數也能擊敗模板函數。

修復事情似乎很棘手。看起來像頭的is_array模板(如果可能的話,從C++ 0x開始,或者如果沒有,則爲Boost)可能會有幫助。

+0

的確,您可以使用enable_if和is_array來獲得您想要的內容。 – 2011-03-02 21:36:10

+0

@Tomalak:有沒有簡單的方法使用C++ 03和Boost?我將舉一個例子,然後意識到我絕對需要rvalue-references,它們不是問題標籤中語言的一部分。 – aschepler 2011-03-02 21:50:31

+0

@aschepler:我用一種涉及平整比賽場地的方法更新了我的答案。 – 2011-03-02 21:51:39

1

這似乎是不同的編譯器。

Mircosoft和Borland都使用const char *版本,而GNU提供了您描述的輸出。

下面是從C++標準的代碼段:

14.8.2.1從一個函數調用推導模板參數 [temp.deduct.call]

模板參數推導由 比較每個完成功能模板 參數類型(稱爲P) 類型的相應參數 該呼叫(稱爲A)如下面描述的 。

如果P不是引用類型:

- 如果A是一個數組類型,由陣列到指針 標準轉換所產生的指針類型(4.2)在 地方A對使用類型扣除; 否則,

- 如果A是一個功能類型,由 函數到指針標準 轉化率(4.3)產生的指針類型代替甲 類型扣被使用;否則,

- 如果A是cv限定類型,則類型扣減將忽略A 類型的頂級cv限定符。

如果P是cv限定類型,則P類型的頂級 級別cv限定符是 ,對於類型推導被忽略。如果P是 引用類型,用P稱爲 類型被用於類型扣除

如下編譯器將建立一個A列表:

Argument:  t     d 
A:   char const[34]  char[34] 

和參數列表P

Parameter:  c     t 
P:   char const*  char const& t[N] 

默認情況下,編譯器應該選擇未引用的參數。由於某種原因,GNU第二次出錯。

+0

不,它不是。你忘記了非模板的首選。如果MS和Borland提供不同的輸出,那麼它們不符合規定。模板類型演繹是完全不相關的。 – 2011-03-02 21:45:25

+0

模板演繹參數不是問題。重載分辨率是。 – aschepler 2011-03-02 21:45:39