16.1 模板定义

16.1.1 函数模板

template <typename T>
int compare(const T &v1, const T &v2)
{
	if (v1 < v2) return -1;
	if (v1 > v2) return 1;
	return 0;
}

实例化函数模板

  1. 调用一个函数模板时,编译器用推断出模板参数来为我们实例化一个特定版本的函数

模板类型参数

  1. 函数类型参数用 typename

非类型模板参数

  1. 表示一个值而非一个类型
  2. 被一个用户提供或编译器推断出的值替代
  3. 必须是常量表达式,在编译时实例化
template<unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M])
{
	return strcmp(p1, p2)
}
template <typename T> inline T min(const T& , const T& );

调用

compare("hi", "mom")
//相当于
// int compare(const char (&p1)[3], const char (&p2)[4])

inline和constexpr的函数模板

放在模板蚕食列表之后,返回类型之前

编写类型无关的代码

  1. 模板中函数是 const 引用,以保证用于不能拷贝的类型
  2. 类型无关和可以移植,小于可用模板库函数less(v1, v2) 类避免类型依赖于小于号

模板编译

  1. 编译器在模板定义时并不生成代码

  2. 实例化(调用)时,编译器才生成代码

  3. 由于模板在实例化时生成代码,会影响编译器如何组织代码和错误何时检查

  4. 函数模板和类模板定义在头文件中

    1. 调用一个函数时,编译器只需要函数声明
    2. 模板实例化时,编译器需要函数模板或类模板成员函数的定义
    3. 综上所述,函数只需声明在头文件中,而模板需要声明和定义在头文件中
  5. 大多数编译器错误在实例化期间报告,编译器会在三个阶段报告错误

    1. 第一阶段是编译模板本时

      • 编译器检查语法(分号或变量拼写等)
    2. 第二阶段是编译器遇到模板使用时

      • 对于函数模板,检查实参数目,检查参数类型是否匹配等
      • 对于类模板,检查用户是否提供正确数目的模板实参
    3. 第三阶段是模板实例化时

      • 只有这个阶段才能发现类型相关的错误
      • 依赖于编译器如何管理实例化,这类错误可能在连接时报告

详见:C++ Primer 第16章 模板和泛型编程