基本介紹
- 中文名:模板參數推導
- 外文名:template argument deduction
- 支持語言:C++
概念,推導類型,推導規則,類型轉發,
概念
模板函式在定義時,用template<>聲明模板參數(或稱模板形參)。調用模板函式時,可以在函式名字後用“< >”顯式指出模板參數(這時稱作模板實參)。例如:
template<class T> void foo(T v1){}; foo<double>(3);
但是,調用模板函式時,如果不顯式指明模板參數,而是根據函式的調用實參去推斷模板實參,這就是模板參數推導。例如上例可進一步考慮情形:
foo(3.14);
編譯器會推導出這種函式調用的模板實參為T->double。
推導類型
編譯器比較函式模板的形參(template parameter)與對應的調用實參(argument used in the function call)的類型,以確定模板參數的類型。形參的類型必須是下述特定情形之一:
T const T volatile T T& T* T[10] A<T> C(*)(T) T(*)() T(*)(U) T C::* C T::* T U::* T (C::*)() C (T::*)() D (C::*)(T) C (T::*)(U) T (C::*)(U) T (U::*)() T (U::*)(V) E[10][i] B<nowiki><i></nowiki> TT<T> TT<nowiki><i></nowiki> TT<C>
說明:
- T, U, V表示模板類型參數
- 10表示任意整數常量
- i表示模板非類型參數
- [i]表示數組界( represents an array bound of a reference or pointer type, or a non-major array bound of a normal array)
- TT表示模板的模板參數(template template argument)
- (T), (U), (V)表示參數列表包含至少一個模板類型參數
- ()表示參數列表不包含模板參數
- <T>表示模板參數列表包含至少一個模板類型參數
- <i>表示模板參數列表包含至少一個模板非類型參數
- <C>表示模板參數列表其模板參數不依賴於模板實參
編譯器可以從上述幾個類型結構的複合類型推導模板參數。
推導規則
對於形如:
template<typename T> int foo(ParamType param);
其模板參數T的類型需要從模板函式的實參與形參依照如下規則推導:
- 首先,如果模板函式的實參表達式是引用,首先去除引用;
- 上一步後,如果剩下的實參表達式有頂層的const且/或volatile限定符,去除掉。
因而,從模板函式的實參表達式,不能自動推導出頂層的CV-qualifiers,也不能自動推導出引用類型,需要顯式指定。
例如:
template<class T> void foo(T arg){ arg=101;} // 函式模板 const int i=102; foo(i); //函式模板實例化為 void foo<int>(int),實參的const限定已被脫去 foo<const int&>(v1);//直接顯示指明模板參數類型 template<class T> void foo(const T& arg);//或者偏特化模板函式
如果形參還帶上&號,聲明為引用類型,則不執行const剝除(const-stripping),例如:
const int i=102; const int &j=i; template<class T> void foo(T& arg) { arg=101;}// 函式模板 foo(j); // 編譯錯誤: 'arg': you cannot assign to a variable that is const
這是因為如果不抑制const剝除,則得到了一個非常量引用型變數,綁定到const變數,這顯然是不可接受的。
實參表達式為數組,模板參數推導的類型為指針。這是因為數組名在實參表達式中自動隱式轉換為首元素地址的右值。例如:
int a[9]; template<class T> void foo(T arg){}; // 函式模板 foo(a);// 函式模板實例化為 void foo<int*>(int*)
另外,C++11標準明確規定不能由模板參數推導出對應實參為std::initializer_list的類型。例如:
template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T
類型轉發
C++11增加了右值引用這一新的數據類型。如:template<class T> void foo(T&& arg);“T&&”並不意味著形參arg的數據類型一定是右值引用。其數據類型既可能是左值引用,也可能是右值引用。依據“引用塌縮規則”,有:
- 如果實參表達式是類型A的左值,則模板參數T的類型為左值引用A&,形參arg的類型為左值引用A&;
- 如果實參表達式是類型A的右值(包括純右值與臨終值),則模板參數T的類型為右值引用A&&,形參arg的類型為y右值引用A&&。
如此,就把實參的值分類情形地傳遞到模板函式內部,據此可再轉發給拷貝語義或移動語義的實現函式。