2012-03-09 51 views
12

我很驚訝,下面的代碼產生了could not deduce template argument for T錯誤:編譯器爲什麼不能從默認參數中推導出模板類型?

struct foo 
{ 
    template <typename T> 
    void bar(int a, T b = 0.0f) 
    { 
    } 
}; 

int main() 
{ 
    foo a; 
    a.bar(5); 

    return 0; 
} 

調用a.bar<float>(5)修復該問題。爲什麼編譯器不能從默認參數中推導出類型?

A template type-parameter cannot be deduced from the type of a function default argument.

在C++ 11:

回答

15

在C++ 03,說明書中明確被用來推斷模板參數(C++ 03§14.8.2/ 17)禁止所述默認參數,可以爲函數模板提供默認模板參數:

template <typename T = float> 
void bar(int a, T b = 0.0f) { } 

不過,需要默認模板參數。如果未提供默認模板參數,則默認函數參數仍不適用於模板參數推演。具體而言,適用於下列(C++ 11 14.8.2.5/5):

The non-deduced contexts are:

...

  • A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
+10

雖然說「因爲標準如此」是一個有效的答案,所以很高興知道其背後的原因。 – 2012-03-09 04:39:16

+1

除了其他原因,函數的不同聲明可以聲明不同的默認參數(我相當肯定這同樣適用於函數模板。) – 2012-03-09 04:42:25

+1

@James:不,不允許聲明不同的默認參數。甚至不允許多個聲明將相同的默認值賦予相同的參數。 8.3.6說:「一個默認的參數不能被後面的聲明重新定義(甚至不會被重新定義)。」當然,這隻適用於非模板功能。對於模板函數,它看起來像默認參數只能在初始聲明中提供。 – 2013-09-03 05:03:33

4

一個很好的理由可能是

void foo(bar, xyzzy = 0); 

類似於一對重載。

void foo(bar b) { foo(b, 0); } 
foo(bar, xyzzy); 

此外,有時有利的是,它重構爲這樣:

void foo(bar b) { /* something other than foo(b, 0); */ } 
foo(bar, xyzzy); 

即使當爲一個寫的,它仍然像在一個兩個功能,均未在任何意義上的「優選的」 。你正在調用單參數函數;雙參數實際上是一個不同的功能。默認參數表示法將它們合併爲一個。

如果重載是有你所要求的行爲,那麼爲了保持一致性,它必須在模板分成兩個定義的情況下工作。這是沒有道理的,因爲那麼扣除將會從不被調用的不相關函數中抽取類型!如果沒有實施,這意味着與「默認論證」相比,重載不同的參數列表長度成爲「二等公民」。

如果重載和違約之間的差異完全隱藏給客戶端,那就好了。

7

實現這一點在總體上會有一些技術上的困難。請記住,模板中的默認參數在需要之前不會被實例化。考慮則:

template<typename T, typename U> void f(U p = T::g()); // (A) 
template<typename T> T f(long, int = T()); // (B) 
int r = f<int>(1); 

這是通過執行(其中包括)以下步驟解決今天:

  1. 嘗試推斷考生模板參數(A)和(B); (A),因此被刪除。
  2. 執行重載分辨率; (B)被選擇
  3. 形式呼叫,實例化所述默認參數

爲了從默認參數來推斷,即默認參數將必須本身完成扣過程之前實例化。這可能會失敗,導致SFINAE環境之外的錯誤。也就是說,一個可能完全不適合打電話的候選人可能會引發錯誤。

+0

聲音對我來說合理。 – 2017-01-11 10:22:21

相關問題