C++模板进阶

C++模板进阶

  • 非类型模板参数
  • 模板特化
    • 模板特化分类
  • 模板分离编译
    • 解决办法
  • 模板总结

非类型模板参数

模板参数分为类型模板参数和非类型模板参数;
类型模板参数:就是在模板参数列表中出现在关键字class或typename之后的参数;
非类型模板参数:出现在模板参数列表的整数,该整数是常量,不可更改,可以作为模板的参数来使用;
类模板:在这里插入图片描述
函数模板:
C++模板进阶_第1张图片
对于类型模板参数来说,我们可以给缺省值,同样的对于非类型模板参数来说,我们也可以给缺省值,模板参数列表的缺省值规则与函数缺省值的规则一样!
C++模板进阶_第2张图片
注意:
1、非类型模板参数必须是整型,不能是浮点数、自定义类型、字符串;
2、那么整型包括那些:int、long int、long long int、char、short等等;、
3、非类型模板参数与#define定义的宏类似,在编译阶段就确定了结果!
非类型模板参数的代表作有STL的容器
在这里插入图片描述
位图
C++模板进阶_第3张图片\

模板特化

通常情况下,我们可以对某些具体类型,做一些特殊的处理,比如对于一个小于类模板,如果模板正常情况的话,该类就是对于两个元素进行比较,但是如果是两个元素是指针类型的话,我们是不希望用指针的原生值(也就是指针本身)去比较的,我们希望用指针指向的内容来进行比较,因此如果在用原来通用的小于模板的来实例化的话,就不再合适,我们希望对于指针类型能够使用特例话的模板来实例化:
C++模板进阶_第4张图片
我们可以来看看运行结果:
C++模板进阶_第5张图片
我们可以看到结果实在变化的,我们希望是piless(pa,pb)是输出的1,可是为什么它会一会输出0,一会输出1呢?很简单,pliess的模板参数是int*,那么piless的operator()函数就是判断两个int*指针的大小,由于这指针是我们从堆上new出来的,而且是分开new的,我们无法保证pa先new出来地址就一定比pb后new出来的下,同理也无法保证pa比pb大!因为pa、pb的空间都是随机分配的!
而且,我们本意也不是想比较这两个指针,我们是想让其根据两个指针指向的内容来做小于判断;
那么现在目前这张方法是不行的,是违背我们的初衷的!有没有什么解决办法?
有的!
1、重新写一个模板类,专门用来处理模板参数为指针的:
C++模板进阶_第6张图片
2、我们不需要在重新写一个类模板,我们原来的类模板不是不能对指针类型做出正确的处理嘛,那么我们在利用模板实例化的对象的时候先判断一下模板参数的类型呗!对于指针类型的模板参数我们做一些特殊处理,不用原来的模板实例化,用经过特殊处理的模板来实例化:
C++模板进阶_第7张图片
因此模板特化,就是在原有模板的基础上,根据模板参数做特殊处理的实现方式;
注意实在原有模板的基础上,不可将原有模板删去!原有模板必须存在!
模板分为:函数模板和类模板;同理:模板特化也分为函数模板特化类模板特化

模板特化分类

模板特化呢分为偏特化全特化;
对于类模板来说,它既支持偏特化也支持全特化;
对于函数模板来说,它只支持偏特化;C++语法不支持函数模板的全特化
1、全特化
类模板的全特化;
全特化:就是模板参数的类型都是特定的,比如:现在我有两个模板参数,当T1模板参数为int时,T2模板参数为double时就用当前模板的一个特化模板来实例化对象;
C++模板进阶_第8张图片
运行结果:
C++模板进阶_第9张图片
注意全特化类模板的语法:
还是要写template,只不过模板参数列表里面不用写参数,在类里面也是使用具体类型,注意类名要指名每个模板参数的类型;
2、偏特化
偏特化:也就是对模板参数中的一部分参数做特殊处理,其他参数还是当作泛型!
a、类模板的偏特化
C++模板进阶_第10张图片
注意语法:模板参数列表里面,对那个参数做了特殊处理,那么那个参数就可以不用出现在参数列表,但是后面的类名后面必须出现;
b、函数模板的偏特化:
函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

C++模板进阶_第11张图片

模板分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式;
假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:
C++模板进阶_第12张图片
如果我们直接就这样去编译的话,编译是会报错的!准确点来说,在编译阶段没问题,但是在链接的时候会出现问题
C++模板进阶_第13张图片
编译器会告诉我们,它找不到该符号对应的链接;
我们来具体分析一下,编译器到底为什么找不到;
1、我们知道编译器是只会编译源文件的不会编译头文件,头文件在预处理阶段就会被展开在源文件中!那么现在我们有两个源文件:a.cpp、main.cpp;
编译器就会对这两个源文件进行编译,在此期间两个源文件的编译都是分离的独立的进行的互不干扰的!
C++模板进阶_第14张图片
以上的事情都是编译器做的!那么在做完这一切过后,如果我们想要得到一个可执行文件的话,那么链接器就要发挥作用了,那么链接器是如何发挥作用的呢?
首先在汇编阶段,每个源文件都会形成符号表!什么是符号表呢?
符号表就是当前源文件对于出现在当前文件的一些符号的统计,并且为这些符号建立链接关系(也就是为这些符号映射一个地址!),这些符号包括哪些?
全局变量、静态遍历、函数名等:
C++模板进阶_第15张图片

解决办法

  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。推荐使用这种。
  2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。
    C++模板进阶_第16张图片

模板总结

【优点】

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

你可能感兴趣的:(C++,c++,开发语言,数据结构)