1. 基本概念
1.1 什么是模板?
模板(Template)是允許函數(shù)或者類通過泛型(generic types)的形式表現(xiàn)或運(yùn)行的特性。
1.2 模板有什么用?
模板可以使函數(shù)或者類只寫一份代碼而對應(yīng)不同的類型。
1.3 模板編程/泛型編程
一種獨(dú)立于特定類型的編碼方式
1.4 模板分類
模板分為函數(shù)模板與類模板兩類。
- 函數(shù)模板(Function template):使用泛型參數(shù)的函數(shù)(function with generic parameters)
- 類模板(Class template):使用泛型參數(shù)的類(class with generic parameters)
2. 函數(shù)模板
- 模板聲明
template <模板形參表> 函數(shù)返回類型 函數(shù)(形參表);
- 模板定義
template <模板形參表>
函數(shù)返回類型 函數(shù)(形參表){
函數(shù)體;
};
例如:
template <typename T> T Max(T a,T b){
return a>b?a:b;
}
- 模板實(shí)例化
函數(shù)(實(shí)參表)
產(chǎn)生模板特定類型的函數(shù)或者類的過程稱為實(shí)例化
調(diào)用函數(shù)模板與調(diào)用函數(shù)完全一致。
- 實(shí)例
最值函數(shù)Max(),Min()
字符串轉(zhuǎn)數(shù)值Ston()
3. 類模板
- 模板聲明
template <模板形參表> class 類名;
- 模板定義
template <模板形參表>
class 類名 {
}
- 模板實(shí)例化
類名<模板實(shí)參表> 對象;
- 模板參數(shù)表
多個模板參數(shù)之間,分割。模板參數(shù),模板參數(shù),... - 模板參數(shù)
- 類型形參
class 類型形參或者typename 類型形參
類模板的聲明與實(shí)現(xiàn)通常都寫在頭文件中,是不能夠分開的。
- 實(shí)例
復(fù)數(shù)類Complex
三角形類Triangle
4. 模板參數(shù)推導(dǎo)/推演(deduction)
模板參數(shù)推導(dǎo)/推演(deduction):由模板實(shí)參類型確定模板形參的過程。
實(shí)例化有兩類:
顯示實(shí)例化:代碼中明確指定類型的實(shí)例化
隱式初始化:根據(jù)參數(shù)類型自動匹配的實(shí)例化
類模板參數(shù)允許自動類型轉(zhuǎn)換(隱式轉(zhuǎn)換);函數(shù)模板參數(shù)不允許自動類型轉(zhuǎn)換(隱式轉(zhuǎn)換)
在模板參數(shù)列表中,class和typename完全一樣。但是在語義上,class表示類,typename代表所有類型(類以及基本類型)。
請盡量使用typename
函數(shù)模板實(shí)參類型不一致問題
template <typename T>
inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
模板實(shí)例化時,
Max(2,2.4)
參數(shù)推導(dǎo)會出現(xiàn)模板實(shí)參類型int與double不一致的錯誤。
解決方法:
- 每個模板參數(shù)獨(dú)立類型
template <typename T , typename U> inline const T& Max(const T& a, const U& b){
return a>b?a:b;
}
注意:這種解決方法還有一個問題,就是返回值只能強(qiáng)制設(shè)置為T或者U,不能自動推導(dǎo)。C++11的后置推導(dǎo)解決這個問題。
template <typename T, typename U>
inline auto Max(const T& a, const U& b)->decltype(a>b?a:b)
{
return a>b?a:b;
}
- 顯示指定模板實(shí)參類型
Max<int>(2,2.4)
或者
Max<double>(2,2.4)
- 實(shí)參強(qiáng)制類型轉(zhuǎn)換
Max(2,static_cast<int>(2.4))
或者
Max(static_cast<double>(2),2.4)
模板參數(shù)推導(dǎo)不允許類型自動轉(zhuǎn)換,模板參數(shù)必須嚴(yán)格匹配。
函數(shù)模板實(shí)例顯示指定模板實(shí)參可以顯示指定模板實(shí)參,也可以不指定(類型自動推導(dǎo)),類模板實(shí)例化必須
5. 特化
- 模板特化(specialization):模板參數(shù)在某種特定類型下的具體實(shí)現(xiàn)稱為模板的特化。模板特化有時也稱之為模板的具體化。
特化作用
- 對于某種特殊類型,可以做特殊處理或者優(yōu)化。
- 避免實(shí)例化類的時候產(chǎn)生詭異行為。
模板特化分類
- 函數(shù)模板特化(Function specializations):對函數(shù)模板的全部模板類型指定具體類型。
- 類模板特化(Class specializations):對類模板的全部或者部分模板類型指定具體類型。
5.1 函數(shù)模板特化
特點(diǎn):函數(shù)模板,卻只有全特化,不能偏特化。
步驟:與類的全特化相同
示例:
template<typename T>
void Func(const T& n){}
// 特化
template<>
void Func(const int& n){}
5.2 類模板特化
特點(diǎn):類模板特化,每個成員函數(shù)必須重新定義。
類模板特化分為兩種
- 全特化(Full specializations):具體指定模板的全部模板參數(shù)的類型。
- 局部特化(Partial specializations):具體指定模板的部分模板參數(shù)的類型。
5.2.1 全特化
步驟:
- 聲明一個模板空參數(shù)列表
template<> - 在類名稱后面的
<>中顯示指定類型。
示例:
// 模板
template<class T>
class Test{};
// 全特化
template<>
class Test<int*>{};
5.2.2 偏特化
偏特化就是部分特化,分為兩種情況
- 個數(shù)特化:只為部分模板參數(shù)指定具體類型(模板參數(shù)個數(shù)變少)
- 范圍特化:模板參數(shù)不變,限制模板參數(shù)的匹配類型(指針、引用、const)
步驟:
- 在一個模板類參數(shù)列表不指定或者指定部分具體類型。
- 在類名稱后面的對應(yīng)類型中顯示指定該類型。
示例:
template<typename T1,typename T2>
class Test{};
- 將模板參數(shù)偏特化為相同類型
template<typename T>
class Test<T,T>{};
- 將一個模板參數(shù)特化成具體類型
template<typename T>
class Test<T,int>{};
- 把兩個類型偏特化成指針類型
template<typename T1,typename T2>
class Test<T1*,T2*>{};
實(shí)例:三元組模版Triple
類模板特化,相當(dāng)于函數(shù)模板的重載
全特化和偏特化的編碼區(qū)別:
全特化的模板參數(shù)列表為空template<>,偏特化的模板參數(shù)列表不為空。
模板原理

模板通常會被編譯兩次
- 實(shí)例化前,檢查模板代碼是否有語法錯誤。
- 實(shí)例化中,檢查模板代碼調(diào)用是否合法。
如何查看模板實(shí)例化的結(jié)果?https://cppinsights.io/
非類型模版參數(shù)
非類型模板的實(shí)參只能是整型常量、枚舉值或者指向外部鏈接對象的指針。
不能使用浮點(diǎn)型、類對象、內(nèi)部鏈接對象的指針。
技巧
函數(shù)模板參數(shù)盡量使用引用類型const &
例如:
template <typename T> inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
類模板
成員函數(shù):只有調(diào)用時才會被實(shí)例化。
靜態(tài)成員:每次類模板實(shí)例化,都會被實(shí)例化。
類實(shí)例化成對象,類模板實(shí)例化成類。
- 類模板:不完整的類,一個或者多個成員類型未確定。
- 函數(shù)模板:不完整的函數(shù),一個或者多個參數(shù)類型未確定。
如何查看模板實(shí)例化的結(jié)果?https://cppinsights.io/
實(shí)例
- 函數(shù)模板重載和特化
#include <iostream>
#include <cstring>
using namespace std;
// 引用類型模板
template <typename T>
bool Equal(const T& a,const T& b){
return a == b;
}
// 特化成浮點(diǎn)型
template<>
bool Equal(const double& a,const double& b){
return abs(a-b) < 1e-6;
}
// -------------------------------------------------
// 指針類型模板(函數(shù)模板重載)
template<typename T>
bool Equal(const T* a,const T* b){
return *a==*b;
}
// 特化成char*
template<>
bool Equal(const char* a,const char* b){
return strcmp(a,b)==0;
}
int main(){
cout << Equal(1,1) << endl;
cout << Equal(1.2,1.2) << endl;
cout << Equal(string("abc"),string("abc")) << endl;
cout << Equal(1,2) << endl;
cout << Equal(1.2,1.21) << endl;
cout << Equal(string("abcd"),string("abc")) << endl;
cout << Equal(1.2,(10.2-9)) << endl;
int arr[] = {1,2,3,1};
cout << Equal(arr,arr+3) << endl; // bool Equal(int*,int*)
cout << Equal("abc","abcd") << endl;
}