一、非类型模板参数
模板参数分为类型形参和非类型形参
- 类型形参:出现在模板参数列表里,跟在class/typename之后的参数类型名称
- 非类型参数:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将参数当作常量来使用
template <class T,size_t N = 100>
class A
{
public:
A()
:_size(0)
{}
//...
private:
T _a[N];
int _size;
};
上面就是一个简单的静态数组的类,N就是一个非类型模板参数
注意:1.浮点数、类对象以及字符串是不允许作为非类型模板参数的
2.非类型的模板参数必须在编译期就能确认结果
二、模板的特化
概念:在原模版的基础上,针对特殊类型所进行特殊化的实现方式,模板的特化分为函数模板特化和类模板特化
1.函数模拟板的特化
- 必须要先有一个基础的函数模板
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型
- 函数形参列表必须和模板函数的基础参数类型完全相同
template<class T>
bool Less(T a, T b)
{
cout << "Less(T a, T b)" << endl;
return a < b;
}
template<>
bool Less<int*> (int* a, int* b)
{
cout << "Less<int*>(int* a, int* b)" << endl;
return *a < *b;
}
//建议直接写出具体函数
/*bool Less(int* a, int* b)
{
cout << "Less(int* a, int* b)" << endl;
return *a < *b;
}*/
int main()
{
int a = 10, b = 20;
cout << Less(a, b) << endl;
cout << Less(&a, &b) << endl;
return 0;
}
2.模板特化
2.1全特化----将模板参数列表中所有的参数都确定化
template <class T1,class T2>
class A
{
public:
A()
{
cout << "class A" << endl;
}
private:
T1 x;
T2 y;
};
//全特化
template <>
class A<int,char>
{
public:
A()
{
cout << "class A<int,char>" << endl;
}
private:
int x;
char y;
};
2.2偏特化
偏特化:任何针对模板参数进一步进行条件限制设计的特化版本,分为部分特化和参数的进一步限制两种表现形式
2.2.1部分特化---将模板参数列表种的一部分参数特化
//以上面的模板为基础的部分特化
template <class T>
class A<T, char>
{
public:
A()
{
cout << "class A<T, char>" << endl;
}
private:
T x;
char y;
};
2.2.2参数的更进一步的限制
template <class T1,class T2>
class A<T1*, T2*>
{
public:
A()
{
cout << "class A<T1*, T2*>" << endl;
}
private:
T1* x;
T2* y;
};
测试样例---大家可以猜一下下面的对象分别用的是哪个模板实例化的
void test()
{
A<int*, int*>a1;
A<int, char>a2;
A<double, char>a3;
A<double, double>a4;
}
三、模板的分离编译
1.什么是分离编译
一个程序(项目)有多个源文件共同实现,而每个源文件单独编译生成目标文件,最终将所有目标文件连接起来形成单一的可执行文件的过程成为分离编译模式
2.模板的分离编译
如上图,一般的函数像上面这样写没有啥问题,但是模板这样写就会出错,为什么呢???因为代码在被编译成可执行程序的过程中,会生成符号表,这里面会有函数和函数地址,当调用函数的时候,就会在里面查找,而模板函数在被实例化之前是没有地址的,也就是不会进入符号表,所以当执行程序的时候,找不到该函数,故报错
(解释一下为什么函数模板没有被实例化,因为每个源文件都是单独编译的,在编译test.cpp文件的时候,由于有头文件的声明,Swap函数被认为是在其他文件中,只要在最终生成的符号表中查找就行,而swap.cpp文件中写的函数模板在等待被实例化,所以不会进入符号表)
所以解决方案有两种,如下
1.在模板定义的地方显示实例化(不推荐,只能一个类型一个类型的实例化),如下图
2.将声明和定义写在一起都放在.h中(或.hpp中,就是.h+.cpp文件结合的意思,和.h文件本质一样)
两种方法本质都是为了让函数模板能够成功实例化
类模板的分离编译同函数模板,这里就不多说了
四、模板的总结
【优点】1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生2. 增强了代码的灵活性【缺陷】1. 模板会导致代码膨胀问题,也会导致编译时间变长2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误