在 C++98/03 標準中,類模板可以有預設的模板參數,如下: template <typename T, typename U = int, U N = 0> struct Foo { // ... }; 但是卻不支持函數的預設模板參數: template <typename T = int> ...
在 C++98/03 標準中,類模板可以有預設的模板參數,如下:
template <typename T, typename U = int, U N = 0>
struct Foo
{
// ...
};
但是卻不支持函數的預設模板參數:
template <typename T = int> // error in C++98/03: default template arguments
void func()
{
// ...
}
現在這一限制在 C++11 中被解除了。上面的 func 函數在 C++11 中可以直接使用,代碼如下:
int main(void)
{
func(); //T = int
return 0;
}
此時模板參數 T 的類型就為預設值 int。從上面的例子中可以看出,當所有模板參數都有預設參數時,函數模板的調用如同一個普通函數。但對於類模板而言,哪怕所有參數都有預設參數,在使用時也必須在模板名後跟隨<>
來實例化。
除了上面提到的部分之外,函數模板的預設模板參數在使用規則上和其他的預設參數也有一些不同,它沒有必須寫在參數表最後的限制。甚至於,根據實際場景中函數模板被調用的情形,編譯器還可以自行推導出部分模板參數的類型。
這意味著,當預設模板參數和編譯器自行推導出模板參數類型的能力一起結合使用時,代碼的書寫將變得異常靈活。我們可以指定函數中的一部分模板參數採用預設參數,而另一部分使用自動推導,比如下麵的例子:
template <typename R = int, typename U>
R func(U val)
{
return val;
}
int main()
{
func(97); // R=int, U=int
func<char>(97); // R=char, U=int
func<double, int>(97); // R=double, U=int
return 0;
}
C++11 標準中,我們可以像 func(97) 這樣調用模板函數,因為編譯器可以根據實參 97 自行推導出模板參數 U 的類型為 int,並且根據返回值 val=97 推導出 R 的類型也為 int;而 func
再次強調,當預設模板參數和自行推導的模板參數同時使用時,若無法推導出函數模板參數的類型,編譯器會選擇使用預設模板參數;如果模板參數即無法推導出來,又未設置其預設值,則編譯器直接報錯。例如:
template <typename T, typename U = double>
void func(T val1 = 0, U val2 = 0)
{
//...
}
int main()
{
func('c'); //T=char, U=double
func(); //編譯報錯
return 0;
}
其中,func('c') 的這種調用方式,編譯器通過實參 'c' 可以推導出 T=char,但由於未傳遞第 2 個實參,因此模板參數 U 使用的是預設參數 double;但 func() 的調用方式是不行的,雖然 val1 設置有預設值,但編譯器無法通過該預設值推導出模板參數 T 的類型。由此不難看出,編譯器的自動推導能力並沒有想象的那麼強大。
總的來說,C++11 支持為函數模板中的參數設置預設值,在實際使用過程中,我們可以選擇使用預設值,也可以嘗試由編譯器自行推導得到,還可以親自指定各個模板參數的類型。