C++初阶 模版进阶
一.非类型模版参数
模板参数分类类型形参与非类型形参。
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
类型模版参数
这里的 T 代表一种类型,比如 int、double、char。
非类型模版参数
这里的 N 不是类型,而是一个整数常量。
这里 10、20 就是非类型模板参数。
注意:Array<10> 和 Array<20> 是两个不同的类型。
常见用途是固定容量数组
T 是类型模板参数,表示数组元素类型。
N 是非类型模板参数,表示数组大小。
非类型模板参数必须是编译期常量:
正确写法:
注意: 浮点数、类对象以及字符串是不允许作为非类型模板参数的
二.模版的特化
1.概念
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理
对于
都没问题
但是有时候某个类型需要特殊处理
如果直接:
比较的是地址,而不是字符串内容。
这时候就需要对 char* 单独处理。
2.函数模版特化
函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
特化后:
输出: 1
注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。
再保留原模版
编译器会优先匹配:
不会去实例化模板。
3.类模版特化
全特化
普通模版
专门针对 int
输出: 普通模板
int特化版本
这里
已经把所有模板参数都确定了
所以叫全特化
双参数类模板
都属于普通模板
偏特化
情况1:固定一个参数
输出: 第二个参数为int
这里T1 仍然可以变化
都能匹配。
因此只固定了一部分参数。
叫偏特化
情况2:固定第一个参数
输出: 第一个参数为int
指针偏特化
输出: 普通版本
指针版本
指针版本
引用偏特化
输出:引用版本
三.模板分离编译
1.什么是模板分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式
2.模版分离编译
模板的声明和定义不能像普通函数一样随便分开放在 .h 和 .cpp 里。
因为模板不是普通代码,它需要在编译期根据具体类型实例化。
如果你把实现写到 .cpp:
然后在 main.cpp 里使用:
这时候可能会链接错误。
原因是:main.cpp 只看到了模板声明:
但看不到模板定义
所以编译器无法在 main.cpp 中实例化出:
而 Stack.cpp 虽然有模板定义,但它没有使用:
所以它也不会生成 Stack<int>::Push 的具体代码。
最后链接时就会报:
也就是:找不到函数实现。
3.解决方法
1.声明和定义都放在头文件里
或者类内声明,类外定义,但都写在 .h 里:
这样 main.cpp 包含头文件后,既能看到声明,也能看到定义,就可以实例化模板。
2.显式实例化
如果你明确知道只会用某几个类型,比如只用 int 和 double,可以这样写。
这样可以
但如果你用
就不行,因为 .cpp 里没有显式实例化:
四.模板总结
优点:
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
缺陷:
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误
