一、非类型模板参数
模板参数有两种:1. 类型形参 2. 非类型形参。
类型形参:出现在模板参数列表中,跟在class或者typename之后的参数。
非类型形参:用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
比如类中需要开辟一个静态数组,但是大小未知,此时就可以使用非类型形参:
另外模板参数也可以写为缺省参数:
注意:
- 浮点数、类对象以及字符串是不允许作为非类型模板参数的(即只允许整形家族:int、long、longlong、char)
- 非类型模板参数必须在编译期就能确认结果,因为编译器在编译阶段需要根据传入的非类型模板参数实例化出对应的类或函数。
二、模板的特化
模板虽然不对类型进行限制,但是并不是所有类型都能得到我们想要的结果,比如这样的函数模板就无法实现我们比较字符串的目的:
这时候就需要对模板进行特化,即在原模板的基础上,针对特殊类型所进行特殊化的实现方式,模板特化中分为函数模板特化与类模板特化。
2.1. 函数模板特化
函数模板的特化步骤:
- 首先必须要有一个基础的函数模板。
- 关键字template后面接一对空的尖括号<>。
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型。
- 函数形参表必须要和模板函数的基础参数类型完全相同,否则不同的编译器可能会报一些奇怪的错误。
比如上面的字符串类型就可以如下特化:
另外,当函数模板和类型匹配的函数同时存在时,编译器会优先调用类型匹配的函数,而不会再根据函数模板实例化出对应的函数的时候,因此可以直接将该函数直接给出,而不是进行特化:
2.2. 类模板化
全特化
全特化即是将模板参数列表中所有的参数都确定化。
偏特化
偏特化就是特化部分参数,或者将参数指定为指针,引用等:
当然,如果存在两个特化的模板都匹配的情况,那就会报错:
三、模板分离编译
一个程序由若干个源文件共同实现,每个源文件单独生成.o
目标文件,最后将所有的目标文件,链接起来生成可执行文件的过程称之为分离编译模式。比如将函数声明写在.h
头文件中,函数定义写在.cpp
文件中,在主文件中包含这个头文件就可以使用其中的函数。
分离编译有以下优点:
- 能够提高编译速度,比如修改了众多文件中的其中一个文件,分离编译只需要单独编译修改后的文件即可,大大节省了编译时间。
- 方便管理和阅读代码
一个整体的文件,阅读起来和管理起来都是比较麻烦的,当文件分为一个个小模块之后,阅读感更好,管理也比较方便
3.1 模板为什么不支持分离编译
程序要运行起来一般要经历以下四个步骤:
- 预处理: 头文件展开、去注释、宏替换、条件编译等。
- 编译: 检查代码的规范性、是否有语法错误等,确定代码实际要做的工作,在检查无误后,将代码翻译成汇编语言。注意:头文件不参与编译,编译器对工程中的多个源文件是分离开单独编译的。
- 汇编: 将编译后的汇编文件翻译成二进制目标文件。
- 链接: 将生成的各个目标文件进行链接,生成可执行文件。
总结起来就是,定义的地方没有实例化,需要实例化的地方只有声明没有定义,最终导致链接错误。
解决办法:
- 将声明和定义放到同一个
.h
文件之中。 - 模板定义的位置显示实例化(不推荐,因为需要自己手动显示实例化一个函数,这样便丧失了模板存在的意义)。
3.2. 模板的优缺点
优点:
- 模板增强了代码的复用性、节省资源、更快的迭代开发,C++的标准模板库(STL)也因此而产生,C++98才出现的模板,有了模板之后才出现的STL库
- 增强了代码的灵活性
相同的函数接口,只是传入的参数类型不一样,只需要传入不同的参数,即可以实例化不同的模板代码。比如排序问题,可以给int 数据排序和cha类型数据排序,没有引入模板,就需要编写两次排序函数,而引入模板之后,就可以在实例化的时候,根据不同的数据类型实例化排序方法。
缺点:
-
有些C++编译器还不支持模板,当使用这些编译器编译含有模板的代码时就会发生不兼容问题。
-
不支持分离编译,所有用基于模板算法的实现必须包含在整个设计的头文件中。