模板的定义

一个模板就是一个创建类或函数的蓝图。当获取到足够的信息时,蓝图将转换为特定的类或函数,这种转换发生在编译时期。C++ 中模板是泛型编程的基础,泛型编程让我们忽略类型的限制来编写代码。C++ 中主要是函数模板和类模板。

模板的编译和链接问题

大多数人会按照如下方式组织非模板代码: 将类或者其他类型声明放在头文件(.hpp、.H、.h、.hh、.hxx)中。 将函数定义等放到一个单独的编译单元文件中(.cpp、.C、.c、.cc、.cxx)。

但是这种组织方式在包含模板的代码中却行不通,例如: 头文件:

  1. // myfirst.h
  2. template<typename T>
  3. void printTypeof (T const&);
  1. // myfirst.cpp
  2. #include <iostream>
  3. #include <typeinfo>
  4. #include "myfirst.h"
  5. template<typename T>
  6. void printTypeof (T const& x) {
  7. std::cout << typeid(x).name() << '\n';
  8. }

在另一个文件中使用该模板

  1. // main.cpp
  2. #include "myfirst.h"
  3. int main() {
  4. double ice = 3.0;
  5. printTypeof(ice);
  6. }

在 c/c++ 中,当编译阶段发现一个符号 (printTypeof) 没有定义只有声明时,编译器会假设它的定义在其他文件中,所以编译器会留一个”坑“给链接器 linker ,让它去填充真正的符号地址。

模板是比较特殊的,需要进行模板参数类型推断,实例化模板,当然也就需要知道函数的定义。当编译器编译 main.cpp 时,它没有找到模板的定义(此时定义变为二进制代码),因此没有生成对应的函数。

解决办法就是我们把模板的声明和定义都放在一个头文件 (hpp 文件)。将接口与实现分离的主要是以二进制形式隐藏实现细节。所以你的模板类必须只代表数据结构而不是算法。

实例化

使用模板时我们隐式或显示地指定模板实参将其绑定在模板参数上,编译器通常会根据函数实参来推断模板参数。类型参数前必须使用关键字 class 或 typename。模板参数接受内置类型或者类类型。

类型推导

C++17之后支持了类模板的推导,C++17也支持了模板函数的 auto 推导