1.非类型模板参数
我们之前的模板参数是类型模板参数,而非类型模板参数是常量,和宏功能类似
但是宏有个缺点,因为同一个宏的常量在一个项目中只有一个值,所以不能满足更加灵活多变的项目需求,但是非类型模板参数就可以通过输入不同的参数解决这个问题
比如我们需要使用一个函数去计算物体落下所需时间,那么就需要一个重力加速度g
粗略计算就是10,在月球上又会再变小。
因为我们的常量会在不同的情景下发生变化,所以我们要求这个常量的改变是简单的,而非类型模板参数刚好可以解决这个问题
注意:当前c++只支持整形,double类型等其他类型都不行
2.模板的特化
一般情况下模板可以很好解决各种数据类型的问题,但是如果遇到一些特殊的数据类型就会导致出现逻辑问题。
比如对于整形,浮点型等数据类型的比较,直接用大于小于符号就可以进行比较,但是如果遇到整形指针,我们想比较的是指针指向的整形数据,而不是指针本身。此时如果还是直接使用大于小于符号比较就会出问题
这里我们的lessfun函数就是对数据直接比较,整形的数据比较没有问题,但是对于指针类型的比较就会出问题,我们想比的是nn1指向的1,nn2指向的2,而不是指针本身
2.1函数模板特化
接下来我们实现函数模板的特化解决这个问题
我们这里就把lessfuc关于int*类型的情况特化处理了,这样子在使用lessfuc函数遇到int*类型时就不会使用指针直接比较大小,而是进入这个特化的模板进行解引用的比较
函数模板的步骤:
1.有一个正常的非特化函数模板,然后在这个模板的基础上再进行特化修改2.将模板的尖括号置空,然后把需要特化的类型用尖括号写在特化模板函数名的后面
3.修改数据类型为特化类型
4.函数参数的关系必须和模板函数一样,否则会出问题
eg:
这里我们如果直接原封不动的替换类型会报错,究其原因是修饰的关系错了
模板中const修饰的是变量本身,而特化模板中修饰的是变量指向的内容
我们将const的位置改动到*右边即可
实际上我们如果需要对某种特定类型使用特殊逻辑,我们会使用普通函数,而不是特化模板。因为使用函数的时候优先级更高的是匹配度,匹配度一样就使用已经存在的函数
由于函数可以利用参数自动匹配,而不是必须显式实例化,所以这个写法也可以实现和函数模板特化一样的功能
2.2类模板特化
(1)全特化:将类的所有模板参数都特化
特化的步骤与函数模板一样
我们给lessfuc类(仿函数)关于int*特化之后,就可以实现int指针指向的内容的正确比较大小判断了
(2)偏特化:将类的部分模板参数特化
原模板类:
第一种:特化出具体类型
特化步骤:
1.将需要特化的模板参数从类模板中删除,然后在类名中用尖括号包含不用特化的类型和需要特化的具体类型
2.修改类中特化的类型
我们把T2特化为int类型,而没有特化T1。
第二种:不特化出具体类型,改变模板类型
特化步骤:
1.在类名中用尖括号包含需要改成的模板类型2.若有需要,改变类中对应类型参数的顺序
总结:我们函数就可以不使用模板特化,而是写一个普通函数
对于类,我们需要写特化模板
3.模板分离编译
分离编译:
项目中若干个源文件共同运行,各个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式
具体过程:
1.(分别进行)预处理:注释删除,展开头文件,宏替换文件从,cpp或.h变成.i文件
2.(分别进行)编译:检查语法正确性,将代码转换成汇编代码
文件从.i变成.s
在这个过程中函数内部代码就变成一个个汇编指令,并存储在一段连续地址中,而调用函数的本质就是call(跳转),跳转就需要知道函数此时的地址。
若跳转的函数在同一个文件且在该函数之前出现,那么可以直接call一个具体的地址。
若出现在该函数后面,或者不在同一个文件中,那么他的编译虽然可以通过,不过call的就是一个“ ?”,而不是一个具体的函数地址。这相当于我们给编译器一个承诺,承诺我们有这样一个函数,后面的过程我们才会兑现这个承诺,把地址给到call。
3.(分别进行)汇编:将汇编代码转成二进制机器码
生成目标文件.o
在这个过程中会有一个符号表负责记录函数的地址,方便后续的链接过程查找函数地址
4.(一起进行)链接:合并成可执行程序,链接函数地址
生成可执行程序.exe
特殊情况:出现模板函数且模板函数声明和定义分离
模板函数距离编译还差一个实例化,而有定义的部分不知道用什么类型实例化,所以既没有编译也没有进入符号表。有声明的地方知道用什么类型实例化,编译器先让他过了,到了链接部分由于该函数没有编译,所以找不到地址,就报错了
解决方法:
方法一:显示实例化
显示实例化可以让模板进行编译并进入符号表,从而链接部分就可以跳转到函数所在位置
方法二:将定义与声明写在同一个头文件
如果代码量不大就直接声明和定义一起写了,代码量较大就把定义写在同一个头文件即可。
进行这个操作后我们在.cpp文件中直接就可以call到地址,链接部分不用再找地址了
4.总结
优点远大于缺点
优点:
1.代码兼容性更高,更灵活
2.复用代码节省资源
缺点:
代码膨胀,编译时间变长。
出现模板错误报错信息混乱,难以定位