c++中的模板
以函数模板为例1
2
3
4template<T>
T sub(T a, T b){
return a - b;
}
函数模板定义了函数的通用公式,减少为不同类型相同功能写重复代码1
2int sub(int a, int b);
float sub(float a, float b);
编译器如何检查类型
以上文中的sub函数为例,如果我们传入不支持减法的类型,编译器能检查出错误吗?比如如果尝试使用1
string s = sub("AA", "BB");
我们都知道字符串是不支持减法的(不考虑操作符重载),那么编译器如何在编译期发现这个错误呢?
说到底,模板就是一种对重复代码的抽象方式。编译器会在实例化模板时为具体类型的时候进行语法检查,也就是说在调用sub("AA", "BB")
的时候,编译器会根据实参的类型string
来进行类型检查,也就能发现我们实例化的函数是有语义错误的。
(模板函数不意味着函数需要支持所有可能的类型,使用模板函数不意味着会发生编译器在编译期无法确定的类型错误)
在.cpp文件中定义模板类会发生错误?
编译器需要在实例化模板函数的时候可见模板函数的定义(不是声明),只是声明无法确定需要实例化函数的函数体1
2//sub.h
template <typename T> sub(float a, float b);
1 | //sub.cpp |
1 |
|
在sub.h中只能看到声明template <typename T> sub(float a, float b);
无法实例化函数,编译器假设其他编译单元中包含sub(int a, int b)
的定义,但是找不到定义,所以抛出undefined reference
的错误。
解决方法
在sub.h
中定义模板函数
缺点
- 编译速度缓慢(头文件过大)
- 无法隐藏代码实现
显式实例化
以上文的sub.h
和 sub.cpp
为例, 为了阅读方便我再次把两个文件copy下来
1 | //sub.h |
1 | //sub.cpp |
1 |
|
上文提到实例化sub
函数的时候,sub.h
中不包含模板函数的定义导致了错误。我们可以想到令其他编译单元(其他.cpp/.obj文件)生成我们需要的实例模板函数。
举例,建立init_sub.cpp
文件,我们令这个编译单元生成我们main函数中需要的sub
函数1
2
3//init_sub.cpp
template int sub(int a, int b);
这样init_sub.cpp
编译单元中就包含了int sub(int a, int b)
main
函数就不会报错了。
导出模板
使用export关键词1
2
3
4//sub.cpp
export template <typename T> sub(float a, float b){
return a - b;
}
但是编译器大多没有实现。
总结
头文件定义模板函数适用于小型项目
显示实例化适用于大型项目加速编译
typename vs. class
//todo
推荐阅读