一、定义模板
- 模板参数列表:template<typeanme Ti, class Ti>
- 模板参数:类型模板参数(typename、class)、非类型模板参数(一个值,特定的类型名)
- 模板实参:绑定到模板参数;可以有默认模板实参
- 模板实例:使用模板实参代替对应的模板参数从而创建出模板的一个特定版本
- 模板编译:编译器遇到模板定义并不生成代码。当实例化出模板的一个特定版本时才会生成代码
- 函数模板:生成函数,编译器可以推断模板实参类型
- 类模板:生成类,编译器不能推断模板实参类型
注:
- 在模板作用域中引用模板类型:记住类模板的名字不是一个类型名。一个类模板中的代码中如果使用了另外一个模板,通常不将一个实际类型(或值)的名字用作其模板实参。而是将模板自己的参数当做被使用模板的实参。
- 类模板的成员函数是一个模板函数,在类外定义时,需要指定和类模板一样的模板参数
- 类与友元各自是否是模板是相互无关的。
- 一个类也可以将另一个模板的每个实例都声明为自己的友元,或者限定的实例为友元
- 可以将模板类型参数声明为友元
- C++语言假定通过作用域访问的名字不是类型。因此,使用typename T::value_type表示T::value_tyep是一个类型。
- 成员模板:本身是模板的成员函数,不能是虚函数。在类外定义时,类目吧你的参数列表在前,后跟成员自己的模板参数列表
- 显式实例化:避免相同的实例出现在多个对象文件中,造成的额外开销。extern template declaration为实例化声明;template declaration为实例化定义。(为什么可以有多个相同的实例:当链接器合并这些目标文件时,它会尝试消除重复的符号,包括相同的模板实例化。这意味着,如果多个翻译单元中有相同的模板实例化,链接器会选择其中的一个实例化,并丢弃其他的。)
- 实例化定义会实例化所有成员
二、可变模板
- 参数包:可变参数模板中可变数目的参数
- 模板参数包:表示零个或多个模板参数。 template<typename T, typename… Args>,Args就是模板参数包。注意,Args前面可以有其他的类型,如这里T。
- 函数参数包:表示零个或多个函数参数。 template<typename T, typename… Args> void foo(const T &t, const Args& … rest),rest就是函数参数包。
- sizeof…运算符:计算包中有多少元素
- 包扩展:将包扩展为其构成元素或其他更复杂的扩展模式
- 转发参数包:使用forward机制将实参传递给其他函数。std::forward<Args>(args)...
注:
1.可以使用一个initializer_list来定义一个可接受可变数目实参的函数,但是所有实参必须具有相同的类型或者可以转换为同一个类型
2.可变参数通常是递归的
看这个调用表就理解为什么是递归的。(t每次都从rest取走一个,这也是包扩展)
3.参数包展开技术、包扩展(parameter pack expansion)来处理每个参数类型都不一样的情况。参数包展开允许你在编译时展开参数包,并对其中的每个参数执行相同的操作
三、模板特例化
- 模板特例化:不是从通用模板实例化得到的类或函数的实例化版本。使用空尖括号template<>定义。
- 类模板部分特例化:类模板的特例化不必为所有模板参数提供实参。可以只指定一部分而非所有模板参数,或是参数的一部分而非全部特性。部分特例化本身是一个模板,使用他时用户还必须为那些在特例化版本中未指定的模板参数提供实参。template<class T> struct A<T&> {};
- 特例化成员:只特例化特定成员函数而不是整个模板