模板
模板被称为c++ 下的编译器多态。最近写模板遇到了一些坑总结下。
模板 声明与定义分离
c++ 惯有的源文件模式都是 将声明写到 .h 文件中,将实现写到 .cpp 文件中。但是对于模板来说,如果我们把 模板类或者模板函数这样分离实现,当链接的时候会报链接错误。
本质上,这个错误因为 当其他源文件 包含 模板的头文件的时候,编译器编译当前源文件时会认为该 模板 已经有了相应实现,具体的实现就在相应的模板源文件中,编译器不会再去实例化相应的模板函数 或者 模板的成员函数 ,它会交给 链接器去链接。
这个时候因为虽然 模板的源文件有了 相应的实现,但是如果 没有使用相应的模板函数 或者 模板类 的话, 编译器并不会给该模板源文件的模板进行实例化 ,所以等链接器链接的时候会报链接错误,如下demo。
templateA.h
template <class T>
class TemplateA {
public:
void Fun();
};
templateA.cpp
template <class T>
void TemplateA<T>::Fun() {}
main.cc
int main()
{
templateA<int> test_a;
test_a.Fun();
return 0;
}
main.cc:(.text+0x32): undefined reference to `TemplateA<int>::Fun()
所以解决方案俩种
- 将模板的实现和定义全放到头文件中
- 在 .cpp 中使用 template 关键字进行显式实例化
templateA.cpp
template <class T>
void TemplateA<T>::Fun() {}
//显示实例化
template class TemplateA<int>;
但是显示实例化虽然一定程度实现了 声明与实现分离,但是这样依赖于调用者的实现,每多一个类型, template.cpp 文件就得多一行相应的实例化声明,接着相应的 template.cpp 就得被重新编译。
派生类继承模板类
有的时候我们想 继承一个模板类的具体的实例化,这样是可以的。当需要重写 模板类的虚函数时,只要给出具体类型的实现即可。
templateA.h
template <typename Key>
class Table {
public:
Table() {}
virtual Fun (Key key) = 0;
};
main.cc
class WoodTable : public Table<int> {
public:
WoodTable()
{}
virtual Fun(int key)
{}
};
模板函数类型推导
参数推导顺序 从 右 到 左 参数以此进行推导。
模板类中的虚函数
- 模板类的成员函数是可以是 虚函数的。
- 但是 成员模板函数不能是虚函数
这俩个很绕口,模板类的成员函数,它可以使用 模板类的模板参数,并且可以虚化(也就是可以是虚函数)。但是在模板类 / 类中定义一个 成员模板函数,把它声明成 虚函数,编译器会报错。
模板类的成员函数虚化
下面的这个模板类的成员函数,虽然它里面也使用了模板