模板的定义
一个模板就是一个创建类或函数的蓝图。当获取到足够的信息时,蓝图将转换为特定的类或函数,这种转换发生在编译时期。C++ 中模板是泛型编程的基础,泛型编程让我们忽略类型的限制来编写代码。C++ 中主要是函数模板和类模板。
模板的编译和链接问题
大多数人会按照如下方式组织非模板代码: 将类或者其他类型声明放在头文件(.hpp、.H、.h、.hh、.hxx)中。 将函数定义等放到一个单独的编译单元文件中(.cpp、.C、.c、.cc、.cxx)。
但是这种组织方式在包含模板的代码中却行不通,例如: 头文件:
// myfirst.h
template<typename T>
void printTypeof (T const&);
// myfirst.cpp
#include <iostream>
#include <typeinfo>
#include "myfirst.h"
template<typename T>
void printTypeof (T const& x) {
std::cout << typeid(x).name() << '\n';
}
在另一个文件中使用该模板
// main.cpp
#include "myfirst.h"
int main() {
double ice = 3.0;
printTypeof(ice);
}
在 c/c++ 中,当编译阶段发现一个符号 (printTypeof) 没有定义只有声明时,编译器会假设它的定义在其他文件中,所以编译器会留一个”坑“给链接器 linker ,让它去填充真正的符号地址。
模板是比较特殊的,需要进行模板参数类型推断,实例化模板,当然也就需要知道函数的定义。当编译器编译 main.cpp 时,它没有找到模板的定义(此时定义变为二进制代码),因此没有生成对应的函数。
解决办法就是我们把模板的声明和定义都放在一个头文件 (hpp 文件)。将接口与实现分离的主要是以二进制形式隐藏实现细节。所以你的模板类必须只代表数据结构而不是算法。
实例化
使用模板时我们隐式或显示地指定模板实参将其绑定在模板参数上,编译器通常会根据函数实参来推断模板参数。类型参数前必须使用关键字 class 或 typename。模板参数接受内置类型或者类类型。
类型推导
C++17之后支持了类模板的推导,C++17也支持了模板函数的 auto 推导