C++ Primer学习笔记
定义模板
函数模板
一个函数模板就是一个公式,可用来生成针对特定类型的函数版本。
template <typename T>
int compare(const T &r1,const T &r2)
{
if(r1<r2) return -1;
if(r2<r1) return 1;
return 0;
}
- 模板以template关键字开始,<>部分表示模板参数列表,由多个模板参数组成,这里
<typename T>
表示模板参数列表。模板定义中,模板参数列表不能为空。 - 实例化函数模板:编译器用推断出的模板参数来实例化一个特定版本的函数。模板的实例:编译器生成的版本。
//实例化出int compare(const int&,const int&)
compare(1,0);
//实例化出int compare(const vector<int>&,const vector<int>&)
vector<int> vec1{1,2,3},vec2{2,1,3}
compare(vec1,vec2);
- 一个非类型参数表示一个值而非一个类型,当模板实例化时,非类型参数被一个***用户提供的或编译器推断出的值***所替代。非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或引用。在模板定义内,模板非类型参数是一个常量值。***非类型模板参数的模板实参必须是常量表达式(因为要允许编译器在编译时实例化模板)***。
//非类型参数
template <unsigned M,unsigned N>
int compare(const char(&r1)[M],const char(&r2)[N])
{
return strcmp(r1,r2);
}
//实例化出int compare(const char(&)[3],const char(&)[4])
compare("hi","mom");
- 编译器遇到模板定义并不生成代码,实例化模板的特定版本时,编译器才会生成代码。为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,模板的头文件通常既包括声明也包括定义。
- 编译器在编译时实例化。
类模板
与函数模板不同,编译器不能为类模板推断参数类型。
- 使用类模板必须提供***显示模板实参***列表,它们被绑定到模板参数,编译器使用这些模板实参来实例化特定的类。
- 一个类模板的每个实例都形成一个独立的类。
- 类模板的名字不是一个类型名,从一个模板生成的类的名字中必须包含其模板实参。
template <typename T>
return-type class-name<T>::member-name(parm-list);
- 默认情况下,一个类模板的成员函数只有当程序用到它时才进行实例化。默认情况下,对于一个实例化了的类模板,其成员只有在使用时才被实例化。
- 当我们处于一个类模板的作用域中时,编译器处理模板自身引用时就好像我们已经提供了与模板参数匹配的实参一样。在一个类模板的作用域内,我们可以直接使用模板名而不必指定模板实参。
- 新标准允许为类模板定义一个类型别名,一个模板类型别名是一族类的别名。
template <typename T> using twin=pair<T,T>;
- 每个类模板的实例都有其自己的static成员实例。相同类模板实例的所有对象则共享这些static成员。
模板参数
- 一个模板参数名的可用范围是在其声明之后,至模板声明或定义结束之前。模板内不能重用模板参数名。
- 模板声明必须包含模板参数。
template <typename T> int compare(const T&,const T&);
template <typename T> class blob;
- 默认情况下,C++语言假定通过作用域运算符访问的名字不是类型。希望通知编译器一个名字表示类型时,必须使用关键字typename,不能使用class。
typename T::value_type a;
- 可以为函数和类模板提供***默认模板实参***。
//为模板参数提供了默认实参
template <typename T,typename F=less<T>>
//并为其对应的函数参数也提供了默认实参
int compare(const T& r1,const T& r2,F f=F());
- 如果一个类模板为其所有模板参数都提供了默认实参,并且如果希望使用这些默认实参,就必须在模板名之后跟一个空尖括号对。
template <typename T=int> class A{}
A<long double> a;
//空<>表示我们希望使用默认类型
A<> b;
成员模板
一个类(无论是普通类还是类模板)可以包含本身是模板的成员函数,这种成员被称为成员模板。
- 成员模板不能是虚函数。
- 当我们在类模板外定义一个成员模板时,必须同时为类模板和成员模板提供模板参数列表。
控制实例化
通过***显式实例化***避免相同的实例出现在多个对象文件中而产生的开销。
- 实例化形式,declaration是一个类或函数声明,其中所有模板参数已被替换为模板实参。
//实例化声明
extern template declaration;
//实例化定义
template declaration;
- 当编译器遇到extern模板声明时,它不会在本文件中生成实例化代码。对每个实例化声明,在程序中某个位置必须有其显示的实例化定义。
- 一个类模板的实例化定义会实例化该模板的所有成员,包括内联的成员函数。在一个类模板的实例化定义中,所用类型必须能用于模板的所有成员函数。
术语
函数模板
- 实例化函数模板
- 模板类型参数
- 非类型模板参数
- inline和constexpr的函数模板
- 编写类型无关的代码
- 模板编译
- 关键概念:模板和头文件
- 大多数编译错误在实例化期间报告
类模板
- 定义类模板
- 实例化类模板
- 在模板作用域中引用模板类型
- 类模板的成员函数
- 类模板成员函数的实例化
- 在类代码内简化模板类名的使用
- 在类模板外使用类模板名
- 类模板和友元
- 一对一友好关系
- 通用和特定的模板友好关系
- 令模板自己的类型参数成为友元
- 模板类型别名
- 类模板的static成员
模板参数
- 模板参数与作用域
- 模板声明
- 使用类的类型成员
- 默认模板实参
- 模板默认实参与类模板
成员模板
- 普通(非模板)类的成员模板
- 类模板的成员模板
- 实例化与成员模板
控制实例化
- 实例化定义会实例化所有成员
效率和灵活性
- 在运行时绑定删除器
- 在编译时绑定删除器