在之前的学习中,我们初步了解了一下模板的内容,
如果想要回顾请跳转C++模板初阶-CSDN博客
今天我们就来进一步学习模板(以及之前提及的模板不能分为.h和.cpp去写也会在今天进行进一步解答),下面就开始我们的学习吧.
补充:以及模板也可以给缺省值(在适配器和仿函数中有所体现)
类和类方法是按需实例化的(不调用不报错,不使用就不会实例化)
非类型模板参数
在以前我们定义的都是类型模板参数,模板也可以定义非类型的参数.
模板参数分类类型形参与非类型形参。
类型形参即: 出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参 : 就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常 量来使用。
非类型模板参数一般用于定义静态数组.见下(在c++11时库里面的array就用了非类型模板参数)
cpp
namespace bite
{
// 定义一个模板类型的静态数组
template<class T, size_t N = 10>
class array
{
public:
private:
T _array[N];
size_t _size;
};
}
int main()
{
array<int> a1;
array<int,100> a2;
array<int,5> a3;
//他们三个是不同的类型,编译器实现了三个不同类的array.
return 0;
}
注:在c++20之前不支持除整型外的非类型模板参数.20之后支持其他的内置类型了.
非类型的模板参数必须在编译期就能确认结果。
c++11时库里面的array<int,10> a1对标的是正常的int a210这种数组.本质是把数组封装到类中(类似自己实现的链表的迭代器).改善了对数组越界的检查.
int a210这种是抽查,并且越界读无法检查出来.
array<int,10> a1进行严格的越界检查(通过operator\[\]进行).不会完成初始化.
但是也不是什么很有用的设计,因为vector也可以开空间.用vector(size_t n, T t)这个构造(用n个t构造vector).
补充一个小点

更好的处理方法就是直接用auto来接受返回值.
但是实例化的就可以正常使用了

模板的特化
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些 错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板.
函数模板的特化(不是很建议使用)


最后一个就要用到特化.一个模版有两套逻辑,如果要比较的类型是特殊的就走特化的那个.


上面的template<>是必须有的.
但函数模板的特化并不是很好用,并且模板和函数可以同时存在,所以一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。
通常情况下会传const引用,如下,此时直接把T换为Date*特化就会出错.
下面才是正确的.
原因:上面模板中的const是修饰left和right的,下面如果直接替换,const就修饰的是*left,放在*后面才修饰left.
最好将该函数直接给出.
类模板的特化
全特化
全特化即是将模板参数列表中所有的参数都确定化.
cpp
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;}
private:
int _d1;
char _d2;
};
void TestVector()
{
Data<int, int> d1;//用第一个
Data<int, char> d2;//用第二个,对特殊的类进行特殊处理
}
偏特化
任何针对模版参数进一步进行条件限制设计的特化版本.(更多使用)依旧是对于如下日期类

偏特化有以下两种表现方式:
部分特化
将模板参数类表中的一部分参数特化.

此时只要第二个参数为int的都会走这个特化的模板.
参数更进一步的限制
偏特是针对模板参数更进一步的条件限制所设计出来的一 个特化版本.(如指针特化,引用特化)



指针特化如果使用typeid().name来测试T1和T2,会得到T1是int,T2是int,而不是指针类型(引用特化也一样).更方便在特化类内定义原本类型的变量,而不是都是指针和引用变量.
模板分离编译
这里就要说下我们之前一直有提到的模板不能声明和定义分离到两个文件中的原因了



编译只会编译.cpp文件,头文件会在.cpp中展开.
模板在调用时才能确定参数实例化类型,在预处理时.h就会在.cpp中展开,编译时编译器不会对模板进行编译因为没办法知道它实例化为什么类型.所以没有对应的函数地址,在调用进行链接时就会出现找不到函数的情况.(只有声明没有定义)

有一下两种解决方法
1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。(推荐使用这种.)
这时声明和定义就一起存在了,调用的地方就有定义,直接进行实例化.
库中也是用这种方法,短的就设置成内联,长的就类内声明,内外定义.
- 模板定义的位置显式实例化。(这种方法不实用,不推荐使用)
很被动,如果只实例化了两种类型,在调用第三种时还是会找不到,需要不断添加显示实例化(下面是显示实例化方法)

模板优缺点总结
【优点】
-
模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
-
增强了代码的灵活性
【缺陷】
-
模板会导致代码膨胀问题,也会导致编译时间变长
-
出现模板编译错误时,错误信息非常凌乱,不易定位错误


