目錄
引言?
一、非類型模板參數
二、類模板的特化?
(一)概念?
(二)函數模板特化?
(三)類模板特化?
1. 全特化?
2. 偏特化?
(四)類模板特化應用示例?
三、模板的分離編譯?
(一)什么是分離編譯?
(二)模板的分離編譯?
(三)解決方法?
四、模板總結?
(一)優點?
(二)缺陷?
引言
在C++編程世界里,模板是一項極為強大且靈活的特性,它能讓我們編寫出通用、可復用的代碼。今天,就讓我們深入探究C++模板的幾個關鍵方面:非類型模板參數、類模板的特化以及模板的分離編譯。
?
一、非類型模板參數
模板參數分為類型形參與非類型形參。類型形參,我們常見于模板參數列表中,通常緊跟在?class?或者?typename?之后。而非類型模板參數則別具一格,它使用一個常量作為類(函數)模板的一個參數,在模板中可當作常量使用。
?
代碼示例
?
cpp ??
namespace bite {// 定義一個模板類型的靜態數組template<class T, size_t N = 10>class array {public:T& operator[](size_t index) { 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;};
}
?注意事項
?
1.?浮點數、類對象以及字符串是不允許作為非類型模板參數的,用整形初始化。
?
2.?非類型的模板參數必須在編譯期就能確認結果。
?
二、類模板的特化
?
(一)概念
?
模板能實現與類型無關的代碼,但遇到特殊類型時,可能會產生錯誤結果,這就需要對模板進行特化。特化即在原模板類基礎上,針對特殊類型進行特殊化的實現方式。模板特化主要分為函數模板特化與類模板特化。
?
(二)函數模板特化
?
特化步驟
?
1.?首先得有一個基礎的函數模板。
?
2.?使用關鍵字?template?后跟一對空的尖括號?<>?。
?
3.?函數名后緊跟一對尖括號,在尖括號中指定需要特化的類型。
?
4.?函數形參表必須要和模板函數的基礎參數類型完全相同,否則不同編譯器可能會報奇怪錯誤。
代碼示例
?
cpp ??
// 函數模板 -- 參數匹配
template<class T>
bool Less(T left, T right) {return left < right;
}// 對Less函數模板進行特化
template<>
bool Less<Date*>(Date* left, Date* right) {return *left < *right;
}int main() {cout << Less(1, 2) << endl;Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl;Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; ?// 調用特化之后的版本,而不走模板生成了return 0;
}
?
一般情況下,如果函數模板遇到不能處理或者處理有誤的類型,為實現簡單,通常直接給出特化版本。函數模板不建議過度特化,因為直接編寫普通函數實現簡單明了,代碼可讀性高。
?
(三)類模板特化
?
1. 全特化
?
全特化即將模板參數列表中所有的參數都確定化。
?
cpp ??
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;
};
?
2. 偏特化
?
偏特化是針對模板參數進一步進行條件限制設計的特化版本,主要有以下兩種表現方式:
?
- 部分特化:將模板參數類表中的一部分參數特化。
?
cpp ??
// 將第二個參數特化為int
template <class T1>
class Data<T1, int> {
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};
?
?
- 參數更進一步的限制:不僅特化部分參數,還針對模板參數做更進一步的條件限制。
?
cpp ??
// 兩個參數偏特化為指針類型
template <typename T1, typename T2>
class Data<T1*, T2*> {
public:Data() { cout << "Data<T1*, T2*>" << endl; }
private:T1* _d1;T2* _d2;
};// 兩個參數偏特化為引用類型
template <typename T1, typename T2>
class Data<T1&, T2&> {
public:Data(const T1& d1, const T2& d2) : _d1(d1), _d2(d2) {cout << "Data<T1&, T2&>" << endl;}
private:const T1& _d1;const T2& _d2;
};
?
(四)類模板特化應用示例
?
以按照小于比較的類模板?Less?為例:
?
cpp ??
#include<vector>
#include <algorithm>template<class T>
struct Less {bool operator()(const T& x, const T& y) const {return x < y;}
};int main() {Date d1(2022, 7, 7);Date d2(2022, 7, 6);Date d3(2022, 7, 8);vector<Date> v1;v1.push_back(d1);v1.push_back(d2);v1.push_back(d3);// 可以直接排序,結果是日期升序sort(v1.begin(), v1.end(), Less<Date>());vector<Date*> v2;v2.push_back(&d1);v2.push_back(&d2);v2.push_back(&d3);// 此處直接排序結果錯誤,因為sort最終按照Less模板中方式比較,只會比較指針,而非指針指向內容// 此時需要特化Less類模板來處理sort(v2.begin(), v2.end(), Less<Date*>());return 0;
}// 對Less類模板按照指針方式特化
template<>
struct Less<Date*> {bool operator()(Date* x, Date* y) const {return *x < *y;}
};
?
三、模板的分離編譯
?
(一)什么是分離編譯
?
一個程序(項目)由若干個源文件共同實現,每個源文件單獨編譯生成目標文件,最后將所有目標文件鏈接起來形成單一的可執行文件,這種模式就是分離編譯模式。
?
(二)模板的分離編譯
?
當模板的聲明與定義分離開,在頭文件中聲明,源文件中定義時,會出現問題。例如:
a.h
?
cpp ??
template<class T>
T Add(const T& left, const T& right);a.cppcpp ??
template<class T>
T Add(const T& left, const T& right) {return left + right;
}main.cppcpp ??
#include "a.h"
int main() {Add(1, 2);Add(1.0, 2.0);return 0;
}
?
在這種情況下,?a.cpp?中編譯器沒看到對?Add?模板函數的實例化,不會生成具體的加法函數;?main.cpp?編譯后在鏈接時找不到?Add<int>?與?Add<double>?的具體代碼,就會報錯。
?
(三)解決方法
?
1.?將聲明和定義放到一個文件“xxx.hpp”里或者?xxx.h?也可以,推薦這種方式。
?
2.?模板定義的位置顯式實例化,但這種方法不實用,不推薦。
?cpp ?? template<class T> T Add(const T& left, const T& right);a.cppcpp ?? template<class T> T Add(const T& left, const T& right) {return left + right; }//?template //int Add(const int& left, const int& right) //?template //double Add(const double& left, const double& right) main.cppcpp ?? #include "a.h" int main() {Add(1, 2);Add(1.0, 2.0);return 0; }
四、模板總結
?
(一)優點
?
1.?模板復用了代碼,節省資源,能實現更快的迭代開發,C++的標準模板庫(STL)就是基于模板產生的。
?
2.?增強了代碼的靈活性,能適應多種數據類型。
?
(二)缺陷
?
1.?模板會導致代碼膨脹問題,編譯時會針對不同類型實例化出多份代碼,也會使編譯時間變長。
?
2.?出現模板編譯錯誤時,錯誤信息非常凌亂,難以定位錯誤根源。
?
希望通過這篇博客,大家能對C++模板有更深入、全面的理解,在今后的編程中能更好地運用模板特性編寫出高質量、可復用的代碼。