1. 基本概念
1.1 什么是模板匾效?
模板(Template)是允許函數(shù)或者類(lèi)通過(guò)泛型(generic types)的形式表現(xiàn)或運(yùn)行的特性。
1.2 模板有什么用坎藐?
模板可以使函數(shù)或者類(lèi)只寫(xiě)一份代碼而對(duì)應(yīng)不同的類(lèi)型价说。
1.3 模板編程/泛型編程
一種獨(dú)立于特定類(lèi)型的編碼方式
1.4 模板分類(lèi)
模板分為函數(shù)模板與類(lèi)模板兩類(lèi)棵磷。
- 函數(shù)模板(Function template):使用泛型參數(shù)的函數(shù)(function with generic parameters)
- 類(lèi)模板(Class template):使用泛型參數(shù)的類(lèi)(class with generic parameters)
2. 函數(shù)模板
- 模板聲明
template <模板形參表> 函數(shù)返回類(lèi)型 函數(shù)(形參表);
- 模板定義
template <模板形參表>
函數(shù)返回類(lèi)型 函數(shù)(形參表){
函數(shù)體;
};
例如:
template <typename T> T Max(T a,T b){
return a>b?a:b;
}
- 模板實(shí)例化
函數(shù)(實(shí)參表)
產(chǎn)生模板特定類(lèi)型的函數(shù)或者類(lèi)的過(guò)程稱(chēng)為實(shí)例化
調(diào)用函數(shù)模板與調(diào)用函數(shù)完全一致歹啼。
- 實(shí)例
最值函數(shù)Max()
,Min()
字符串轉(zhuǎn)數(shù)值Ston()
3. 類(lèi)模板
- 模板聲明
template <模板形參表> class 類(lèi)名;
- 模板定義
template <模板形參表>
class 類(lèi)名 {
}
- 模板實(shí)例化
類(lèi)名<模板實(shí)參表> 對(duì)象;
- 模板參數(shù)表
多個(gè)模板參數(shù)之間,
分割玄渗。模板參數(shù),模板參數(shù),...
- 模板參數(shù)
- 類(lèi)型形參
class 類(lèi)型形參
或者typename 類(lèi)型形參
類(lèi)模板的聲明與實(shí)現(xiàn)通常都寫(xiě)在頭文件中座菠,是不能夠分開(kāi)的。
- 實(shí)例
復(fù)數(shù)類(lèi)Complex
三角形類(lèi)Triangle
4. 模板參數(shù)推導(dǎo)/推演(deduction)
模板參數(shù)推導(dǎo)/推演(deduction):由模板實(shí)參類(lèi)型確定模板形參的過(guò)程藤树。
實(shí)例化有兩類(lèi):
顯示實(shí)例化:代碼中明確指定類(lèi)型的實(shí)例化
隱式初始化:根據(jù)參數(shù)類(lèi)型自動(dòng)匹配的實(shí)例化
類(lèi)模板參數(shù)允許自動(dòng)類(lèi)型轉(zhuǎn)換(隱式轉(zhuǎn)換);函數(shù)模板參數(shù)不允許自動(dòng)類(lèi)型轉(zhuǎn)換(隱式轉(zhuǎn)換)
在模板參數(shù)列表中浴滴,class
和typename
完全一樣。但是在語(yǔ)義上也榄,class
表示類(lèi)巡莹,typename
代表所有類(lèi)型(類(lèi)以及基本類(lèi)型)司志。
請(qǐng)盡量使用typename
函數(shù)模板實(shí)參類(lèi)型不一致問(wèn)題
template <typename T>
inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
模板實(shí)例化時(shí)甜紫,
Max(2,2.4)
參數(shù)推導(dǎo)會(huì)出現(xiàn)模板實(shí)參類(lèi)型int
與double
不一致的錯(cuò)誤。
解決方法:
- 每個(gè)模板參數(shù)獨(dú)立類(lèi)型
template <typename T , typename U> inline const T& Max(const T& a, const U& b){
return a>b?a:b;
}
注意:這種解決方法還有一個(gè)問(wèn)題骂远,就是返回值只能強(qiáng)制設(shè)置為T
或者U
囚霸,不能自動(dòng)推導(dǎo)。C++11的后置推導(dǎo)解決這個(gè)問(wèn)題激才。
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í)參類(lèi)型
Max<int>(2,2.4)
或者
Max<double>(2,2.4)
- 實(shí)參強(qiáng)制類(lèi)型轉(zhuǎn)換
Max(2,static_cast<int>(2.4))
或者
Max(static_cast<double>(2),2.4)
模板參數(shù)推導(dǎo)不允許類(lèi)型自動(dòng)轉(zhuǎn)換拓型,模板參數(shù)必須嚴(yán)格匹配。
函數(shù)模板實(shí)例顯示指定模板實(shí)參可以顯示指定模板實(shí)參瘸恼,也可以不指定(類(lèi)型自動(dòng)推導(dǎo))劣挫,類(lèi)模板實(shí)例化必須
5. 特化
- 模板特化(specialization):模板參數(shù)在某種特定類(lèi)型下的具體實(shí)現(xiàn)稱(chēng)為模板的特化。模板特化有時(shí)也稱(chēng)之為模板的具體化东帅。
特化作用
- 對(duì)于某種特殊類(lèi)型压固,可以做特殊處理或者優(yōu)化。
- 避免實(shí)例化類(lèi)的時(shí)候產(chǎn)生詭異行為靠闭。
模板特化分類(lèi)
- 函數(shù)模板特化(Function specializations):對(duì)函數(shù)模板的全部模板類(lèi)型指定具體類(lèi)型帐我。
- 類(lèi)模板特化(Class specializations):對(duì)類(lèi)模板的全部或者部分模板類(lèi)型指定具體類(lèi)型。
5.1 函數(shù)模板特化
特點(diǎn):函數(shù)模板愧膀,卻只有全特化拦键,不能偏特化。
步驟:與類(lèi)的全特化相同
示例:
template<typename T>
void Func(const T& n){}
// 特化
template<>
void Func(const int& n){}
5.2 類(lèi)模板特化
特點(diǎn):類(lèi)模板特化檩淋,每個(gè)成員函數(shù)必須重新定義芬为。
類(lèi)模板特化分為兩種
- 全特化(Full specializations):具體指定模板的全部模板參數(shù)的類(lèi)型。
- 局部特化(Partial specializations):具體指定模板的部分模板參數(shù)的類(lèi)型蟀悦。
5.2.1 全特化
步驟:
- 聲明一個(gè)模板空參數(shù)列表
template<>
- 在類(lèi)名稱(chēng)后面的
<>
中顯示指定類(lèi)型媚朦。
示例:
// 模板
template<class T>
class Test{};
// 全特化
template<>
class Test<int*>{};
5.2.2 偏特化
偏特化就是部分特化,分為兩種情況
- 個(gè)數(shù)特化:只為部分模板參數(shù)指定具體類(lèi)型(模板參數(shù)個(gè)數(shù)變少)
- 范圍特化:模板參數(shù)不變熬芜,限制模板參數(shù)的匹配類(lèi)型(指針莲镣、引用、const)
步驟:
- 在一個(gè)模板類(lèi)參數(shù)列表不指定或者指定部分具體類(lèi)型涎拉。
- 在類(lèi)名稱(chēng)后面的對(duì)應(yīng)類(lèi)型中顯示指定該類(lèi)型瑞侮。
示例:
template<typename T1,typename T2>
class Test{};
- 將模板參數(shù)偏特化為相同類(lèi)型
template<typename T>
class Test<T,T>{};
- 將一個(gè)模板參數(shù)特化成具體類(lèi)型
template<typename T>
class Test<T,int>{};
- 把兩個(gè)類(lèi)型偏特化成指針類(lèi)型
template<typename T1,typename T2>
class Test<T1*,T2*>{};
實(shí)例:三元組模版Triple
類(lèi)模板特化的圆,相當(dāng)于函數(shù)模板的重載
全特化和偏特化的編碼區(qū)別:
全特化的模板參數(shù)列表為空template<>
,偏特化的模板參數(shù)列表不為空半火。
模板原理
模板通常會(huì)被編譯兩次
- 實(shí)例化前越妈,檢查模板代碼是否有語(yǔ)法錯(cuò)誤。
- 實(shí)例化中钮糖,檢查模板代碼調(diào)用是否合法梅掠。
如何查看模板實(shí)例化的結(jié)果?https://cppinsights.io/
非類(lèi)型模版參數(shù)
非類(lèi)型模板的實(shí)參只能是整型常量店归、枚舉值或者指向外部鏈接對(duì)象的指針阎抒。
不能使用浮點(diǎn)型、類(lèi)對(duì)象消痛、內(nèi)部鏈接對(duì)象的指針且叁。
技巧
函數(shù)模板參數(shù)盡量使用引用類(lèi)型const &
例如:
template <typename T> inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
類(lèi)模板
成員函數(shù):只有調(diào)用時(shí)才會(huì)被實(shí)例化。
靜態(tài)成員:每次類(lèi)模板實(shí)例化秩伞,都會(huì)被實(shí)例化逞带。
類(lèi)實(shí)例化成對(duì)象,類(lèi)模板實(shí)例化成類(lèi)纱新。
- 類(lèi)模板:不完整的類(lèi)展氓,一個(gè)或者多個(gè)成員類(lèi)型未確定。
- 函數(shù)模板:不完整的函數(shù)脸爱,一個(gè)或者多個(gè)參數(shù)類(lèi)型未確定遇汞。
如何查看模板實(shí)例化的結(jié)果?https://cppinsights.io/
實(shí)例
- 函數(shù)模板重載和特化
#include <iostream>
#include <cstring>
using namespace std;
// 引用類(lèi)型模板
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;
}
// -------------------------------------------------
// 指針類(lèi)型模板(函數(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;
}