在之前的博客中我们初次接触到了泛型编程和模板的概念 【C++】模板初阶,当时只讲了一些基本的用法,现在做一些补充
1. 非类型模板参数
模板参数分为类型形参和非类型形参
类型形参:出现在模板参数列表中,跟在class或者typename之后的参数类型的名称
非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当作常量来使用。
注意:1. 这个常量的类型只能是整形家族,浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型模板参数必须要在编译节点就能确认结果
举个例子:
假设我们现在定义一个模板类型的静态数组
//按照C语言的方法,需要#define一个容量 #define N 10; template<class T> class Array { public: //... private: T _a[N]; }; //但是在C++中,就可以使用非类型模板参数 template<class T, size_t N = 10>//这里也可以像函数参数一样给缺省值 class Array { public: //... private: T _a[N]; };
补充:array
在C++11中,新增了一个容器:array
这个容器在底层和静态数组没有差别,是一个存在栈上的固定大小的顺序容器。array使用文档。
这是array的一些接口,由于是静态数组,所以不支持push_back等操作。
那为什么会有array这个类的出现呢?在array出现之前,数组不是用的也挺好?这是因为C语言对于数组越界的处理是抽查,不是很严格,实现array用来代替掉C语言的静态数组,增强对数组越界的检查等操作。除此之外,把静态数组也用容器封装起来,体现了C++的封装性,提高整个程序的可读性、抽象性、兼容性。
2. 模板的特化
1. 概念
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板
void Test() { cout << Less(1, 2) << endl; // 可以比较,结果正确 Date d1(2023, 4, 30); Date d2(2023, 5, 1); cout << Less(d1, d2) << endl; // 可以比较,结果正确 Date* p1 = new Date(2023, 4, 30); Date* p2 = new Date(2023, 5, 1); cout << Less(p1, p2) << endl; // 可以比较,结果错误 cout << p1 << endl; cout << p2 << endl; }
可以看到,在大多数情况下,Less都能正常运转,但是在某些特殊的场景下,由于比较的逻辑有些许不同,所以最终没办法给出正确答案,此时,我们想对这种特殊情况进行特殊处理,就要对模板进行特化,即在原模版的基础上,针对特殊类型进行特殊化的处理
模板特化分为函数模板特化和类模板特化
2. 函数模板特化
函数模板特化的步骤
- 必须要先有一个基础的函数模板
- 关键字template后面跟一对空的尖括号<>
- 函数名后面跟一对尖括号,尖括号里面指定需要特化的类型
- 函数形参表必须要和函数模板的基础参数类型完全相同,否则会报一堆奇怪的错误
针对上述的示例,我们可以进行以下的模板特化
template<class T> bool Less(T left, T right) { cout << "Less(T left, T right)" << endl;//这里为了方便观察,我们对函数调用进行一下标识 return left < right; } //对Less函数模板进行特化 template<> bool Less<Date*>(Date* left, Date* right) { cout << "Less<Date*>(Date* left, Date* right)" << endl; return *left < *right; }
但是,对于这种情况,我们可以直接使用函数重载的方式来解决,重载一个或者多个形参即可。所以一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出 (函数重载)。
template<class T> bool Less(T left, T right) { cout << "Less(T left, T right)" << endl; return left < right; } //函数重载 bool Less(Date* left, Date* right) { cout << "Less(Date* left, Date* right)" << endl; return *left < *right; }
如上,使用函数重载的方式反而更加清晰简明,所以一般来说使用到函数模板特化的情况很少,对于普通情况,不建议使用函数模板特化。