一、非类型模板参数
我们先来举一个例子:
当我们用宏去控制同种类型数组的大小时,无法做到完全的统一,总会有浪费或是不够。
此时我们的非类型模板参数就发挥作用了
Tips:
1.通常给非类型模板参数赋缺省值,这样开辟一个数组就无比的方便了
2.这里的非类型模板参数赋值时,仅可为整数
二、模板的特化
首先回顾一下模板的概念:模板就是编译器自动给我实例化出一份符合逻辑的代码。但是总有些情况,编译器无法给出正确的逻辑。
比如此时:当我们比较P1和P2这两个字符数组时,我们发现结果并不是相等的,而字符串常量p3和p4是相同的
原因:
首先字符常量存在于进程地址空间的常量区,并且只存在一份。本质我们p3和p4指向的是同一个空间地址。而编译器在实例化的时候,T实例化为指针的右值。显然是相等的。
然而字符数组在栈上开辟了两个完全不同的空间,实例化时也是实例出数组指针。很明显此时两个数组的地址是不同的。所以得到这种结果。
我们想要比较两个字符串是否相等,应该采用strcpy这样的函数来比较字符的ascll值,而不是指针。所以这里的实例化就不尽人意。
此时模板特化就来发挥作用了。
模板特化有很多种方式,我们先来看看函数模板的特化
我们不给模板参数,直接进行特化,这样这份代码就会自动优先匹配这个已经被我们特化后的函数。
我们再来看看类模板的 特化
这两个类实例化后结果显而易见,会与最匹配的模板进行匹配实例化。
以上介绍的都是全特化,我们还有偏特化的概念,意义也很清楚,就是不全部特化。
这里为什么d1不匹配偏特化呢?这里的设计为:先匹配全特化,在匹配偏特化,最后匹配无特化。
除了这些内置类型的特化,还有指针特化 引用特化
如果此时变量类型为指针或是引用,就直接实例化特化的类。如果一个为指针,一个为引用,那么就回去匹配没有特化的原生类,前提是你没有专门特化出一个(*,&)这样的类。
匹配优先度最高的
三、模板不支持分离编译
当我们将模板的声明和定义分开,一个放在.h一个放在.cpp中,那么此时会发生链接错误
原因:
我们在讲底层逻辑之前,先来背诵一下c语言编译过程
1.预处理:头文件展开,条件编译,宏替换,去注释等
2.编译:将.c代码翻译成汇编代码,同时生成符号表,并检查语法错误
3.汇编:将汇编代码生成二进制文件
4.链接:动静态链接 ,多文件链接
此时我们可以发现,链接错误一定是发生在最后一步。
编译过程:main.i -->main.s 时,我们的main函数会去调用add函数
这时候会根据函数名命名规则:比如我们的add函数
call _Z3Addii(?) 括号中是函数的地址,但是由于我们分离编译,main函数中只能确定函数名,检查参数匹配等语法问题,在链接的时候,才会找到这个地址。
由于我们的模板定义在其他文件,就造成我们在main函数中一直没有类模板的定义,而只有声明,无法实例化。有定义的地方我们没法实例化,因为实例化过程发生在编译阶段,有定义的地方在链接阶段。而当到了链接的时候,尽管有了定义,由于实例化阶段已经过了,编译器就会报连接错误。
解决方法:
1.显式实例化
这样很不推荐,用一个实例化一个。
2.推荐:不要分离编译,实例化的地方给定义