用于实现泛型编程
给定一个模板(蓝图),可以生成多个基于此模板的实例,即生成一个新的实例
定义模板
类模板
需要显式传入模板参数
模板类成员函数定义:
template <typename> class Blob; template <typename T> class Blob { public: // ... Blob(); Blob (std::initializer_list<T>); private: std::shared_ptr<std::vector<T>> data; }; template <typename T> Blob<T>::Blob() : data(std::make_shared<std::vector<int>>()) {} template<typename T> Blob<T>::Blob(std::initializer_list<T> il) : data(std::make_shared<std::vector<T>>(il)) {}
同样,模板类成员函数若没被实例化则不会被实例化
在类的作用域里才能省略一些模板参数,在外就不行:
template <typename T> class BlobPtr { public: // 省略了T BlobPtr& operator++(); }; template <typename T> // 这里不能省 BlobPtr<T>& BlobPtr<T>::operator++() { // ... }
模板友元
template <typename> class Pal; template <typename> class C2; class C { // 用类C实例化的Pal是C 的一个友元 friend class Pal<C>; // Pal2 的所有实例都是C的友元,这种情况无须前置声明 template <typename T> friend class Pal2; }; template <typename T> class C2 { // C2的每个实例将相同实例化的Pal声明为友元 friend class Pal<T>; // Pal 的模板声明必须在作用域之内 // Pal2 的所有实例都是C2的每个实例的友元,不需要前置声明 template <typename X> friend class Pal2; // Pal3 是一个非模板类,它是C2 所有实例的友元 friend class Pal3; // 不需要 Pal3的前置声明 // 不需要前置声明operator friend bool operator==(const C2<T>&, const C2<T>&); };
模板类型别名
typedef pair<int, int> pii; template<typename T> using twin = pair<T, T> twin<double> area; // 部分模板参数 template <typename T> using partNo = pair<T, unsigned>; partNo<string> books; // books 是一个pair<string, unsigned>
函数模板
- 模板类型参数
// typename和class在声明模板类型参数时是等价的 template <typename T, class U> int calc (const T&, const U&); // 实例化使用时,不需要把T显示写出来,编译器会进行实参推导 calc(1, 2.2);
- 非类型模板参数
- 表示一个值而非类型,此时用类型关键字而非typename / class声明
- 实参可以是常量表达式整型,也可以是具有静态生命期的指针或引用
// 一种用法是表示数组元素个数,处理同一类型不同元素数量的数组 template<unsigned N, unsigned M> int compare(const char (&p1) [N], const char (&p2) [M]) { return strcmp(p1, p2); } // 实例化的版本:int compare (const char (&p1) [3], const char (&p2) [4]) compare("hi", "mom");
成员模板
给非模板类定义一个模板方法,在工具类中会用得比较多
class DebugDelete { public: explicit DebugDelete(std::ostream& _os = std::cerr) : os(_os) {} template <typename T> void operator() (T *p) const { os << "deleting unique_ptr" << std::endl; delete p; // 删除指针 } private: std::ostream& os; }; int* p = new int(10); DebugDelete() (p);
模板类也可以定义模板方法:
template <typename T> class Blob { template <typename It> Blob(It b, It e); //... }; template <typename T> // 类的类型参数 template <typename It> // 构造函数的类型参数 Blob<T>::Blob(It b, It e): data (std::make shared<std::vector<T>>(b, e)) { }
模板编译
编译器遇到模板定义时不生成代码,使用时才会生成对应版本的实例(实例化阶段)
所以与函数/类成员函数不同,一般将模板定义放在头文件当中
错误报告
编译和使用时不能检查出类型相关的问题,实例化时才能发现,依赖于编译器如何管理实例化,可能在链接时才报告
因此模板程序应该尽量减少对实参类型的要求
作用域访问符(::)访问type和static变量
T::size_type * p;
产生二义性:声明指向T::size_type类型的指针还是让静态变量size_type去乘p?
编译器默认为后者,故如果要使用模板类的类型名称则在前加上typename关键字
template <typename T> typename T::value_type top(const T& c) { if (!c.empty()) { return c.back(); } else { return typename T::value_type(); } }
显式实例化
extern 关键字修饰模板,表示不会在本文件 (.o) 生成实例化代码下面展示一种避免多个文件重复生成同一种模板实例的项目结构:
- 头文件声明并定义模板
- 源文件声明extern模板
// templateBuild.h #pragma once #include <vector> #include <initializer_list> // 类模板 template <typename T> class Blob { public: Blob(std::initializer_list<T> il); Blob(const Blob&); T& operator[](size_t index); private: std::vector<T> data; }; // 函数模板 template <typename T> int compare(const T& a, const T& b);
// templateBuild.cpp #include "templates.h" // 显式实例化定义 template class Blob<std::string>; // 实例化整个类模板 template int compare<int>(const int&, const int&); // 实例化函数模板 // 类模板成员实现 template <typename T> Blob<T>::Blob(std::initializer_list<T> il) : data(il) {} template <typename T> Blob<T>::Blob(const Blob& src) : data(src.data) {} template <typename T> T& Blob<T>::operator[](size_t index) { return data[index]; } // 函数模板实现 template <typename T> int compare(const T& a, const T& b) { return (a < b) ? -1 : (b < a) ? 1 : 0; }
#include "templates.h" // 显式实例化声明 (extern) extern template class Blob<std::string>; // 类模板声明 extern template int compare<int>(const int&, const int&); // 函数模板声明 int main() { // 使用声明过的实例化 (代码不在本文件生成) Blob<std::string> sa1 = {"Hello", "World"}; // 使用initializer_list Blob<std::string> sa2 = sa1; // 使用拷贝构造函数 // 自动实例化 (无extern声明) Blob<int> a1 = {0,1,2,3,4,5,6,7,8,9}; // 实例化Blob<int>和initializer_list构造函数 Blob<int> a2(a1); // 实例化拷贝构造函数 // 使用声明过的函数实例化 int result = compare(a1[0], a2[0]); // 调用compare<int> return 0; }
泛化与特化
template<typename TK, typename TV> // 泛 template<> class/struct xx<TK, TV> // 全特化 template<typename TV> class/struct xx<bool, TV> // 数量偏特化 template<typename T> class/struct xx<T*> // 范围偏特化 template<T> class/struct xx<const T*>

模板实参推断
尾置返回类型
当不知道确切的返回类型,但是知道这个类型与参数的关系时,可以用尾置返回类型:
template <typename It> auto fcn(It beg, It end) -> decltype(*beg) { return *beg; }
类型转换标准库
进行类型转换的标准库模板类:定义于<type_traits>
