文章目錄
- 十一、模板進階
- 1. 非類型模板參數
- 2. 按需實例化
- 3. 模板的特化
- 類模板的特化
- 4. 模板的分離編譯
- 未完待續
十一、模板進階
1. 非類型模板參數
模板參數分為類型形參和非類型形參 。類型形參即:出現在模板參數列表中,跟在class或者typename之類的參數類型名稱 。非類型形參,就是用一個常量作為類(函數)模板的一個參數,在類(函數)模板中可將該參數當成常量來使用 。
// 第一個形參為類型形參,第二個缺省形參為非類型形參
template<class T, size_t N = 10>
#include<iostream>
using namespace std;template<class T, size_t N>
class arr
{// N可以用來分配初始空間
};int main()
{arr<int, 10> a1;arr<int, 1000> a2;return 0;
}
在C++20之前,只支持整形做非類型模板參數 。
2. 按需實例化
類模板在實例化這個類的時候,會按需實例化(調用了哪個成員函數就實例化哪個函數) 。
#include<iostream>
using namespace std;template<class T, size_t N>
class arr
{
public:T& operator[](size_t index){// 該函數參數不匹配size(1);return _array[index];}const T& operator[](size_t index)const{return _array[index];}size_t size()const { return _size; }bool empty()const { return 0 == _size; }private:T _array[N];size_t _size;
};int main()
{// 不去調用 operator[] 時,檢查不出來錯誤arr<int, 10> a1;arr<int, 1000> a2;return 0;
}
當調用出錯的部分的函數時才能檢查出錯誤:
int main()
{// 不去調用 operator[] 時,檢查不出來錯誤arr<int, 10> a1;arr<int, 1000> a2;// 調用operator[]a1[1];return 0;
}
3. 模板的特化
通常情況下,使用模板可以實現一些與類型無關的代碼,但對于一些特殊類型的可能會得到一些錯誤的結果。比如說我們有這樣一個函數模板:
template<class T>
bool less(T left, T right)
{return left < right;
}
該函數模板可以比較大小,但是假如有人錯誤的傳入 地址 當實參,這里必然會出現不確定的結果。如果我們想要傳地址也讓其結果正確的話,我們就可以使用 模板的特化 。特化就是將模板特殊化,在原模板類的基礎上,針對特殊類型所進行特殊化的實現方式。即新增一個特化后的模板。
#include<iostream>
using namespace std;// 函數模板
template<class T>
bool Less(T left, T right)
{return left < right;
}// 特化后的模板
template<>
bool Less<int*>(int* left, int* right)
{return left < right;
}
此時,要是實例化的參數更符合特化就使用特化模板進行實例化,否則使用普通的函數模板進行實例化。
函數模板的特化步驟:
- 必須要先有一個基礎的函數模板
- 關鍵字template后面接一對空的尖括號<>
- 函數名后跟一對尖括號,尖括號中指定需要特化的類型
- 函數形參表: 必須要和模板函數的基礎參數類型完全相同,如果不同編譯器可能會報一些奇怪的錯誤。
一般情況下如果函數模板遇到不能處理或者處理有誤的類型,為了實現簡單通常都是將該函數直接給出,即直接實現一個特殊的函數構成重載。
bool Less(Date* left, Date* right)
{return *left < *right;
}
該種實現簡單明了,代碼的可讀性高,容易書寫,因此函數模板不建議特化。
類模板的特化
類模板特化也分為 全特化 和 偏特化 。
全特化即是將模板參數列表中所有的參數都確定化。偏特化是任何針對模版參數進一步進行條件限制設計的特化版本
template<class T1, class T2>
class Data
{
public:Data(){cout << "Data<T1, T2>" << endl;}
private:T1 _d1;T2 _d2;
};// 全特化
template<>
class Data<int, char> // 參數都全部給定
{
public:Data(){cout << "Data<int, char>" << endl;}
private:int _d1;char _d2;
};// 半特化/偏特化
template<class T>
class Data<T, char> // 不是全特化的特化
{
public:Data(){cout << "Data<T, char>" << endl;}
private:int _d1;char _d2;
};
類模板的偏特化還可以這樣使用:
//兩個參數偏特化為指針類型
template <class T1, class T2>
class Data<T1*, T2*>
{
public:Data(){cout << "Data<T1*, T2*>" << endl;}
private:T1 _d1;T2 _d2;
};
偏特化并不僅僅是指特化部分參數,而是針對模板參數更進一步的條件限制所設計出來的一個特化版本 。
4. 模板的分離編譯
模板不建議聲明和定義分離 。當聲明和定義分離時,聲明處有調用,所以知道模板實例化的類型,但是沒有實現功能,定義處沒有調用,可以實現功能但是不知道實例化的類型,所以定義處在編譯時,并不會生成地址,導致鏈接時找不到地址從而產生鏈接錯誤。
解決方法有兩種:①模板定義的位置顯式實例化。(這種方法不實用,不推薦使用)②不進行聲明和定義分離,將定義也放在.h頭文件里 。