c++ generic

c++中的模板

以函数模板为例

1
2
3
4
template<T>
T sub(T a, T b){
return a - b;
}

函数模板定义了函数的通用公式,减少为不同类型相同功能写重复代码

1
2
int 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
2
3
4
//sub.cpp
template <typename T> sub(float a, float b){
return a - b;
}
1
2
3
4
5
#include <stdio.h>
#include <sub.h>
int main(){
int d = sub(1, 2); // 需要实例化模板函数
}

在sub.h中只能看到声明template <typename T> sub(float a, float b);无法实例化函数,编译器假设其他编译单元中包含sub(int a, int b)的定义,但是找不到定义,所以抛出undefined reference的错误。

解决方法

sub.h中定义模板函数

缺点

  • 编译速度缓慢(头文件过大)
  • 无法隐藏代码实现

显式实例化

以上文的sub.hsub.cpp为例, 为了阅读方便我再次把两个文件copy下来

1
2
//sub.h
template <typename T> sub(float a, float b);
1
2
3
4
//sub.cpp
template <typename T> sub(float a, float b){
return a - b;
}
1
2
3
4
5
#include <stdio.h>
#include <sub.h>
int main(){
int d = sub(1, 2); // 需要实例化模板函数,error
}

上文提到实例化sub函数的时候,sub.h中不包含模板函数的定义导致了错误。我们可以想到令其他编译单元(其他.cpp/.obj文件)生成我们需要的实例模板函数。

举例,建立init_sub.cpp文件,我们令这个编译单元生成我们main函数中需要的sub函数

1
2
3
//init_sub.cpp
#include "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

推荐阅读