1. 模板参数分类
模板参数分成两种:
一种是 非类型形参 和 类型形参
类型形参:
举例
template<class T>struct less{bool operator()(const T& x, const T& y){return x > y;}};
非类型形参:
举例
template<size_t x>class arry{private:int _arr[x];};
这里数组的大小可以被固定住,取决于 x
注意:
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量(不可以被修改)来使用 ,常量是整型类型(如:size_t ,int ,long , char 等等)非类型的模板参数必须在编译期就能确认结果
2. 模板的特化
对于某些特殊的类型,使用模板可能会得到一些的错误的结果
如:日期类的比较
举例
#include<iostream>using namespace std;template<class T>bool less(const T x, const T y){ return x > y;};int main(){ Data t1(2024,3,6); Data t2(2021,6,7); cout << less(t1,t2) << endl; //这种比较得到的是正确的 Data* p1 = new Data(2024,3,6); Data* p2 = new Data(2021,6,7); cout << less(p1,p2) << endl; //这种比较结果不一定正确}
分析:
p1 和 p2 都是动态开辟出来的空间,实际上 less 比较的是它们的地址的高低
a. 函数模板特化
函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
代码举例
template<class T>bool less(const T x,const T y){ return x > y;};template<>bool less<Data *>(const Data *x,const Data *y){return *x > *y;};
注意:
一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出
代码举例
bool less(const Data *x,const Data *y){return *x > *y;};
这样的代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时直接给出,因此函数模板不建议特化
b. 类模板特化
(1)全特化
全特化即是将模板参数列表中所有的参数都确定化
代码举例
template<class T1, class T2>class AA{public:AA(){cout << "T1 T2" << endl;}private:T1 _a;T2 _b;};template<>class AA<int,char>{public:AA(){cout << "int char" << endl;}private:int _a;char _b;};void test2(){AA<int, int> t1;AA<int, char> t2;}
运行结果:
(2)偏特化
偏特化有两种:
一种是 部分特化: 将模板参数类表中的一部分参数特化
代码举例
template<class T1, class T2>class AA{public:AA(){cout << "T1 T2" << endl;}private:T1 _a;T2 _b;};template<class T1>class AA<T1,char>{public:AA(){cout << "T1 char" << endl;}private:T1 _a;char _b;};void test2(){AA<int, int> t1;AA<int, char> t2;}
运行结果:
另一种是 参数更进一步的限制
代码举例
template<class T1, class T2>class AA{public:AA(){cout << "T1 T2" << endl;}private:T1 _a;T2 _b;};template<class T1,class T2>class AA<T1*,T2*> // 限制 T1,T2 是指针类型{public:AA(){cout << "T1* T2*" << endl;}private:T1* _a;T2* _b;};void test2(){AA<int, int> t1;AA<int*, char*> t2;}
运行结果:
注意:
特化必须在原模板的基础之上,它不是全新的模板
3. 模板分离编译
模板不支持分离编译(声明和定义不在一个文件)
a. 失败原因
分析原因:
预处理阶段:
头文件展开,去掉注释,替换宏,条件编译
编译:
检查语法,生成汇编代码(一串指令)
汇编:
将汇编代码转换成二进制机器码
链接:
合成生成可执行文件
由此我们直到,在编译的时候,无法实例化模板,因为模板参数的类型是无法确定的,也无法生成汇编代码
b. 解决方案
声明和定义都放在同一个.h文件中
代码举例
test.h
namespace lhy{template<class T>class Add{public:Add():x(T()), y(T()){}int add();private:T x;T y;};template<class T>int Add<T> ::add(){return x + y;}}
test.cpp
using namespace std;#include "test.h"int main(){lhy::Add<int> t;cout << t.add() << endl;}
运行结果:
4. 模板总结
【优点】
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误