这个章节关于模板的学习目标有三个:
非类型模板参数
类模板的特化
模板总结
1.非类型模板参数:
我们之前用到的模板都是类型模板参数,例如:
但我们有时候也需要使用非类型模板参数,因为我们如果想让数组a中存放10个数据,数组b中存放100个数据该怎么办呢?如果只是用宏来改变N的值是不可行的,我们需要是用非类型模板参数:
template<class T,size_t N=10>
class Array
{
private:
T a[N];
};
int main()
{
Array<int,10> a;
Array<double,100> b;
return 0;
}
这里的非类型模板的使用和c++11中array的实现相似:(是用前要包头文件array)
为什么要设计出array呢?原因就是和静态数组相比,它的检查更加严格。
静态数组检查的规则:越界读不检查,越界写抽查。
array检查的规则:如果越界就直接assert警告!(我们底层实现过就非常了解)
但非类型模板参数有一个特点:它是个整型常量,基本只适合于整型。
当我们的非类型的模板参数不为整数时,程序运行错误:
template<class T,size_t N>
void Func(const T& n)
{
cout << N << endl;
}
int main()
{
Func<int, 10>(1); //这种是正确的
Func<int, 10.5>(10.5); //错误
}
此外它不仅是一个整形,它也必须是一个常量,不可以传变量给它!!
2.模板的特化
2.1函数模板的特化
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T 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. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
template<>
bool Less<Date*>(Date* left,Date* right)
{
return *left < *right;
}
2.2类模板的特化:
2.21类模板的全特化:
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }
};
template<> //类模板的全特化
class Data<double, double>
{
public:
Data() { cout << "Data<double, double>" << endl; }
};
int main()
{
Data<int, int> d1;
Data<int, char> d2;
Data<double, double>d3;
return 0;
}
运行结果:
由此我们可以看出当我们调用时传入的模板参数正好与全特化相符,则编译器会自动调用全特化的而不是再通过推演从普通模板实例化出来。
2.22模板类的半特化/偏特化
概念非常简单,我们通过一个例子来说明:
template<class T>
class Data<T, double>
{
public:
Data() { cout << "Data<T1, double>" << endl; }
};
int main()
{
Data<int, double>d3;
return 0;
}
代码的运行结果:
由此我们可以看出半特化就是一半是需要编译器自己推演出来的,另一半是已经确定好的类型。
不管模板参数中不确定的类型,如果模板参数中确定的类型被匹配上了就会匹配那个半特化的版本。
如果传入的参数既符合全特化又符合半特化, 编译器会自动匹配全特化的。
总结出一句话:编译器能偷懒就偷懒,能不推演参数类型就不推演,越舒服它就越怎么来。
偏特化还可以对某一个类型进行限制:()
运行的结果:
这也算是一种偏特化。
3.模板的总结:
【优点】
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误