1. 基本概念
1.1 什么是模板?
模板(Template)是允許函數或者類通過泛型(generic types)的形式表現或運行的特性。
1.2 模板有什么用?
模板可以使函數或者類只寫一份代碼而對應不同的類型。
1.3 模板編程/泛型編程
一種獨立于特定類型的編碼方式
1.4 模板分類
模板分為函數模板與類模板兩類。
- 函數模板(Function template):使用泛型參數的函數(function with generic parameters)
- 類模板(Class template):使用泛型參數的類(class with generic parameters)
2. 函數模板
- 模板聲明
template <模板形參表> 函數返回類型 函數(形參表);
- 模板定義
template <模板形參表>
函數返回類型 函數(形參表){
函數體;
};
例如:
template <typename T> T Max(T a,T b){
return a>b?a:b;
}
- 模板實例化
函數(實參表)
產生模板特定類型的函數或者類的過程稱為實例化
調用函數模板與調用函數完全一致。
- 實例
最值函數Max()
,Min()
字符串轉數值Ston()
3. 類模板
- 模板聲明
template <模板形參表> class 類名;
- 模板定義
template <模板形參表>
class 類名 {
}
- 模板實例化
類名<模板實參表> 對象;
- 模板參數表
多個模板參數之間,
分割。模板參數,模板參數,...
- 模板參數
- 類型形參
class 類型形參
或者typename 類型形參
類模板的聲明與實現通常都寫在頭文件中,是不能夠分開的。
- 實例
復數類Complex
三角形類Triangle
4. 模板參數推導/推演(deduction)
模板參數推導/推演(deduction):由模板實參類型確定模板形參的過程。
實例化有兩類:
顯示實例化:代碼中明確指定類型的實例化
隱式初始化:根據參數類型自動匹配的實例化
類模板參數允許自動類型轉換(隱式轉換);函數模板參數不允許自動類型轉換(隱式轉換)
在模板參數列表中,class
和typename
完全一樣。但是在語義上,class
表示類,typename
代表所有類型(類以及基本類型)。
請盡量使用typename
函數模板實參類型不一致問題
template <typename T>
inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
模板實例化時,
Max(2,2.4)
參數推導會出現模板實參類型int
與double
不一致的錯誤。
解決方法:
- 每個模板參數獨立類型
template <typename T , typename U> inline const T& Max(const T& a, const U& b){
return a>b?a:b;
}
注意:這種解決方法還有一個問題,就是返回值只能強制設置為T
或者U
,不能自動推導。C++11的后置推導解決這個問題。
template <typename T, typename U>
inline auto Max(const T& a, const U& b)->decltype(a>b?a:b)
{
return a>b?a:b;
}
- 顯示指定模板實參類型
Max<int>(2,2.4)
或者
Max<double>(2,2.4)
- 實參強制類型轉換
Max(2,static_cast<int>(2.4))
或者
Max(static_cast<double>(2),2.4)
模板參數推導不允許類型自動轉換,模板參數必須嚴格匹配。
函數模板實例顯示指定模板實參可以顯示指定模板實參,也可以不指定(類型自動推導),類模板實例化必須
5. 特化
- 模板特化(specialization):模板參數在某種特定類型下的具體實現稱為模板的特化。模板特化有時也稱之為模板的具體化。
特化作用
- 對于某種特殊類型,可以做特殊處理或者優化。
- 避免實例化類的時候產生詭異行為。
模板特化分類
- 函數模板特化(Function specializations):對函數模板的全部模板類型指定具體類型。
- 類模板特化(Class specializations):對類模板的全部或者部分模板類型指定具體類型。
5.1 函數模板特化
特點:函數模板,卻只有全特化,不能偏特化。
步驟:與類的全特化相同
示例:
template<typename T>
void Func(const T& n){}
// 特化
template<>
void Func(const int& n){}
5.2 類模板特化
特點:類模板特化,每個成員函數必須重新定義。
類模板特化分為兩種
- 全特化(Full specializations):具體指定模板的全部模板參數的類型。
- 局部特化(Partial specializations):具體指定模板的部分模板參數的類型。
5.2.1 全特化
步驟:
- 聲明一個模板空參數列表
template<>
- 在類名稱后面的
<>
中顯示指定類型。
示例:
// 模板
template<class T>
class Test{};
// 全特化
template<>
class Test<int*>{};
5.2.2 偏特化
偏特化就是部分特化,分為兩種情況
- 個數特化:只為部分模板參數指定具體類型(模板參數個數變少)
- 范圍特化:模板參數不變,限制模板參數的匹配類型(指針、引用、const)
步驟:
- 在一個模板類參數列表不指定或者指定部分具體類型。
- 在類名稱后面的對應類型中顯示指定該類型。
示例:
template<typename T1,typename T2>
class Test{};
- 將模板參數偏特化為相同類型
template<typename T>
class Test<T,T>{};
- 將一個模板參數特化成具體類型
template<typename T>
class Test<T,int>{};
- 把兩個類型偏特化成指針類型
template<typename T1,typename T2>
class Test<T1*,T2*>{};
實例:三元組模版Triple
類模板特化,相當于函數模板的重載
全特化和偏特化的編碼區別:
全特化的模板參數列表為空template<>
,偏特化的模板參數列表不為空。
模板原理
模板通常會被編譯兩次
- 實例化前,檢查模板代碼是否有語法錯誤。
- 實例化中,檢查模板代碼調用是否合法。
如何查看模板實例化的結果?https://cppinsights.io/
非類型模版參數
非類型模板的實參只能是整型常量、枚舉值或者指向外部鏈接對象的指針。
不能使用浮點型、類對象、內部鏈接對象的指針。
技巧
函數模板參數盡量使用引用類型const &
例如:
template <typename T> inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
類模板
成員函數:只有調用時才會被實例化。
靜態成員:每次類模板實例化,都會被實例化。
類實例化成對象,類模板實例化成類。
- 類模板:不完整的類,一個或者多個成員類型未確定。
- 函數模板:不完整的函數,一個或者多個參數類型未確定。
如何查看模板實例化的結果?https://cppinsights.io/
實例
- 函數模板重載和特化
#include <iostream>
#include <cstring>
using namespace std;
// 引用類型模板
template <typename T>
bool Equal(const T& a,const T& b){
return a == b;
}
// 特化成浮點型
template<>
bool Equal(const double& a,const double& b){
return abs(a-b) < 1e-6;
}
// -------------------------------------------------
// 指針類型模板(函數模板重載)
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;
}