模板
template大汇总
范型
如何实现泛型?
- 函数重载
- 模板
- 类模板
- 函数模板
1 |
|
上述代码中,中typename
是用来定义模板参数关键字,也可以使用class
。
重载存在的问题
重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数。
代码的可维护性比较低,一个出错可能所有的重载均出错。
函数模板
函数模板是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们本该重复的事情交给了编译器。
函数模板的实例化
隐式实例化——让编译器根据实参推演模板参数的实际类型。
显式实例化——在函数名后的<>中指定模板参数的实际类型。例如:
1
my_FuncName<type>(date, date);
类模板
允许以一个类当作蓝本,无需重复代码,创建管理着不同类型数据的类。
1 |
|
类模板的实例化
1 |
|
模板参数的匹配原则
个非模板函数可以和一个同名的函数模板同时存在。并且在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数,那么将选择模板。
模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
函数模板特化
函数模板特化
目的,函数模板对于特殊的类型,生成特殊的方法。但对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。
使用方法
1 |
|
- 必须要先有一个基础的函数模板。
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型。
- 对于函数形参表,必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
类模板特化
- 全特化:全特化即是将模板参数列表中所有的参数都确定化。
1
2
3
4
5
6
7
8
9
10//全特化
//假设已有一个Date函数模板
template<>
class Date<int, char> {
public:
Date() {cout << "Data<int, char>, 全特化" << endl;}
private:
int _d1;
char _d2;
}; - 偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。
- 部分特化
- 参数限制
1 |
|
完整实验代码
1 |
|
模板分离编译
须知:
模板函数的代码其实并不能直接编译成二进制代码,需要被实例化。
如果有一个项目涉及多个源文件,将所有目标文件链接起来形成单一的可执行文件的过程称为分离式编译模式。
模板类和模板函数的实现必须在实例化模板的地方可见。
如果对于编译过程有想更多了解,这里提供另一篇博客。GCC编译过程
如果当一个模板的声明在一个test.h
文件中,但是其实例化在一个test.cpp
文件中。并且在main.cpp
中进行实例化,那么会出现如下的情况:
1 |
|
对于
test.cpp
文件来说,由于它内部没有模板实例化的过程,(test.obj中)所有A模板的信息并不可见,即不会记录有关A::f
的信息。对于
main.cpp
来说,需要关于A::f
的具体实现,但在test.obj
中找不到,在test.h
中也找不到。故报错
通常解决方案:
- 将声明和定义放到一个文件。
指定位置实例化
- 显式实例化是一种机制,用于确保模板的实例化只在一个地方发生,从而避免重复定义的问题。 MyTemplate.cpp 文件中生成一次,而在其他源文件中不会重复生成,从而避免了代码膨胀和重复定义问题
例如:
1 |
|
注意
- 注意:类模板中函数放在类外进行定义时,需要加模板参数列表。
- 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
- 非类型的模板参数必须在编译期就能确认结果。