2010-10-29 100 views
20

下面的代碼無法通過編譯,這個編譯器錯誤的考慮是什麼?爲什麼默認參數不能爲明確的模板專門化指定?

template<class T> void f(T t) {}; 

template<> void f<char>(char c = 'a') {} 

錯誤消息:默認參數不允許在一個函數模板

+0

小備註:函數定義後不需要分號。 – vitaut 2010-10-29 09:02:40

+1

沒有真正回答這個問題,但是不是更清晰/更容易使用重載而不是專業化嗎?有關重載與專業化的討論,請參閱Herb Sutter的這些文章:[C/C++用戶期刊文章](http://www.gotw.ca/publications/mill17.htm)和[GotW#49](http:/ /www.gotw.ca/gotw/049.htm)。 – 2010-10-29 13:40:55

+0

+1 to @Luc。如果你只是做'void f(char c ='a'){}'它編譯得非常好。 – 2010-10-29 13:49:01

回答

21

我認爲一個明確的分工,這個錯誤的理由是由於在函數模板中的默認參數適用於事實它也是專業化的,你不能在C++中多次定義默認參數。

考慮以下幾點:

#include <iostream> 

template<class T> void f(T t = 'a') {} 

template<> void f<char>(char c) 
{ 
    std::cout << c << std::endl; 
} 

int main(int argc, char **argv) 
{ 
    f<char>(); 
} 

這將打印a這意味着專業化被稱爲與主模板中定義的默認參數。

如果你需要爲每個專業化不同的默認參數,你可以使用的方法如下所示:

#include <iostream> 

template<class T> 
struct default_arg 
{ 
    static T get() { return T(); } 
}; 

template<class T> void f(T t = default_arg<T>::get()) {} 

template<> 
struct default_arg<char> 
{ 
    static char get() { return 'a'; } 
}; 

template<> void f<char>(char c) 
{ 
    std::cout << c << std::endl; 
} 

int main(int argc, char **argv) 
{ 
    f<char>(); 
} 
+0

這不是很好..這就是說,所有類型'T'必須能夠用char初始化。或者有一個適當的演員操作符來表示字符。 – 2010-10-29 09:04:37

+0

@Kiril Kirov:這只是一個例子來說明這一點(爲什麼編譯器會給出這個錯誤)。 – vitaut 2010-10-29 09:09:01

+0

是的,但仍然..無論如何:+1 - 對於想法,編輯後添加(: – 2010-10-29 09:16:47

14

C++ 98§12.7/ 21「默認函數參數,不得在指定的...顯式專門化功能模板「。

關於基本原理,我認爲它必須處理一個始終針對主模板進行解析的調用。忽略主模板所需參數的調用在更改查找規則時無法解析。

+0

+1引用相關部分的標準。從來沒有擅長它=) – vitaut 2010-10-29 11:15:35

+1

確實,明確的專業化沒有找到通過名稱查找。模板參數推導是在函數模板上完成的,如果僅由'f()'調用,則不能推導出'T'。它應該如何選擇'char'。有人可能會說,我們可以做'f ()',如果存在提供默認參數的'f '的顯式特殊化,它就會被使用。但我看不到這一點,它只會使模板論證扣除進一步複雜化。 – 2010-10-29 13:28:44

2

要使用哪個特定模板實例的選擇基於提供的參數的類型。因此,顯式特化的選擇是通過提供一個char參數來實現的 - 只有在這一點上,默認參數(如你所編寫的)纔會發揮作用(它是多餘的)。

只有在模板聲明本身上提供默認參數纔有意義。缺點是你必須自己指定適當的專業化(這首先消除了使用默認參數的一些優點)。

爲了達到(我相信)你想要的行爲,請使用以下內容。

template<class T> void f(T t) {} 

template<> void f<char>(char c) {} 

void f() { f('a'); }