1. 非类型模板参数
#include<iostream> using namespace std; #define N 1000 namespace zjw { // 定义一个模板类型的静态数组 template<class T> 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=N; }; }
我们需要两个对象,分别数组大小为10,和1000的话,只用上面一个类是不够的,两个类给不同的N值.
这里我们可以用非类型模板参数来解决这个问题,只需要一个类。
在这之前有一个知识点
#include<iostream> using namespace std; #include<iostream> using namespace std; #define N 1000 namespace zjw { // 定义一个模板类型的静态数组 template<class T> class array { public: T& operator[](size_t index) { return _array[index]; } const T& operator[](size_t index)const { size(1);//这里故意写错 return _array[index]; } size_t size()const { return _size; } bool empty()const { return 0 == _size; } private: T _array[N]; size_t _size = N; }; } int main() { zjw::array<int>st1; cout << st1.size() << endl; }
size(1);//这里故意写错,但是在实例化st1,它只对要用的函数实例化,并不会检查其他类成员函数内部有没有错误。在operator[]中,size(1);//这里故意写错,就不会报错。而编译器只会对大体的结构进行检查,比如说哪个类成员函数少一个分号。
模板参数分类类型形参与非类型形参。
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
#include<iostream> using namespace std; namespace zjw { // 定义一个模板类型的静态数组 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=N; }; }
浮点数、类对象以及字符串是不允许作为非类型模板参数的。c++20可以
非类型的模板参数必须在编译期就能确认结果。
#include<iostream> using namespace std; namespace zjw { template<string str> class A { }; } int main() { zjw::A<"11111">st3; }
2. 类模板的特化
上篇文章我们使用仿函数来给日期类比较大小,现在我们可以使用函数模板来给日期类比较大小
先定义一个日期类
class Date { public: friend ostream& operator<<(ostream& _cout, const Date& d); Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} bool operator<(const Date& d)const { return (_year < d._year) || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day); } bool operator>(const Date& d)const { return (_year > d._year) || (_year == d._year && _month > d._month) || (_year == d._year && _month == d._month && _day > d._day); } private: int _year; int _month; int _day; };
然后实现函数模板比较大小
函数模板 template<class T> bool Less(T left, T right) { return left < right; }
主函数
int main() { Date d1(2024, 2, 10); Date d2(2024, 3, 10); cout << Less(d1, d2) << endl; }
此时d1<d2返回1.
实例化过程如下:
如果我们要给日期类指针进行比较大小
这里我们可以用特化
函数模板的特化步骤:
- 必须要先有一个基础的函数模板
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型
- 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
class Date { public: friend ostream& operator<<(ostream& _cout, const Date& d); Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} bool operator<(const Date& d)const { return (_year < d._year) || (_year == d._year && _month < d._month) || (_year == d._year && _month == d._month && _day < d._day); } bool operator>(const Date& d)const { return (_year > d._year) || (_year == d._year && _month > d._month) || (_year == d._year && _month == d._month && _day > d._day); } private: int _year; int _month; int _day; }; template<class T> bool Less(T left, T right) { return left < right; } // 特化,针对某些特殊类型可以进行特殊处理 template<> bool Less<Date*>(Date* left, Date* right) { return *left < *right; } int main() { Date d1(2024, 2, 10); Date d2(2024, 3, 10); Date* p1 = new Date(2022, 7, 7); Date* p2 = new Date(2022, 7, 8); cout << Less(p1,p2) << endl; }
或者这样类似函数重载,匹配最符合的,有现成的使用现成的
template<class T> bool Less(T left, T right) { return left < right; } 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; } }; int main() { Data<int, char> d2; }
半特化->部分特化
将模板参数类表中的一部分参数特化。
template<class T1, class T2> class Data { public: Data() { cout << "Data<T1, T2>" << endl; } private: T1 _d1; T2 _d2; }; // 半特化/偏特化 template<class T1> class Data<T1, char> { public: Data() { cout << "Data<T1, char>" << endl; } };
半特化->参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版
本。
template<class T1, class T2> class Data { public: Data() { cout << "Data<T1, T2>" << endl; } private: T1 _d1; T2 _d2; }; // 半特化/偏特化,不一定是特化部分参数,可能是对参数的进一步限制 template<class T1, class T2> class Data<T1*, T2*> { public: Data() { cout << "Data<T1*, T2*>" << endl; } };
template<class T1, class T2> class Data { public: Data() { cout << "Data<T1, T2>" << endl; } private: T1 _d1; T2 _d2; }; template<class T1, class T2> class Data<T1&, T2*> { public: Data() { cout << "Data<T1&, T2*>" << endl; } };
5. 模板的分离编译
首先声明定义不分离:不会出现问题
声明定义分离:
链接出现问题,原因是在源.cpp中调用st1.size()会call这个函数的地址,在编译时候,去array.h中发现他只有声明,编译器会以为函数地址会在其他cpp中,等到链接的时候在call size函数地址,但是等到链接时,在cpp之间是分离的,,所以源.cpp中st1实例化的参数<int,10>不会给array.cpp中<T,N>;模板没有实例化,所以array.cpp中的size()函数没有地址,出现了链接错误。
解决方法:显示实例化
反汇编观察size()函数地址,call size()函数地址说明要调用该函数