第16章 模板与泛型编程
16.1 定义模板
假如要编写函数比较两个类型的值,实际要为每个类型编写
16.1.1 函数模板
模板定义以关键字 template 开始,后跟一个模板参数列表,这是一个逗号分隔的一个或多个模板参数的列表,用<>包围 模板参数列表不能为空
实例化函数模板
当调用一个函数模板时,编译器通常用函数实参来为我们推断模板实参。 即,调用compare时,根据实参类型推断T为intasdf
1
2
3
4
5
6
7
8
template <typename T>
int compare(const T &v1,const T &v2)
{
if (v1 < v2) return -1;
if (v1 > v2) return 1;
return 0;
}
cout << compare(1,0) << endl;
编译器生成的版本通常被称为模板的实例
模板类型参数
1
2
3
4
5
6
7
8
template <typename T>
T foo(T* p)
{
T tmp = *p;
return tmp;
}
template <typename T,class U>....
typename与class含义相同,可以交换使用。
非类型模板参数
一个非类型参数表示一个值而非一个类型。 当一个模板被实例化,非类型参数被编译器推断出的值所代替。 例如: 第一个模板参数表示第一个数组的长度,第二个表示第二个数组长度
1
2
3
4
5
6
7
template<unsigned N,unsigned M>
int compare(const char (&p1)[N],const char (&p1)[M])
{
returen strcmp(p1,p2);
}
compare("hi","mom");
编译器实例化N和M
非类型模板参数的模板实参必须时常量表达式
inline和constexpr的函数模板
inline和constexpr放在模板参数列表之后,返回类型之前:
编写类型无关的代码
模板程序应该尽量减少对实参类型的要求。
模板编译
当编译器遇到一个模板定义时,它并不生成代码,只有我们实例化出模板的一个特定版本时,编译器才会生成代码。
函数模板和类模板成员函数的定义通常放在头文件中
类定义和函数声明放在头文件中,而普通函数和类的成员函数的定义放在源文件中
16.1.2 类模板
定义类模板
实例化类模板
显式模板实参 Blob
一个类模板的每个实例都形成一个对立的类。
在模板作用域中引用模板类型
类模板和友元
为了引用(类或者函数)模板的一个特定实例,必须首先声明模板本身
1
template <typename> class BlobPtr;//声明
通用和特定的模板友好关系
一个类也可以将另一个模板的每个实例都声明为自己的友元,或者限定特定的实例为友元。 为了让所有实例成为友元,友元声明中必须使用与类模板本身不同的模板参数
令模板自己的类型参数成为友元
1
2
3
4
5
6
新标准中:
template <typename Type> class Bar{
friend Type;//将访问权限授予用来实例化Bar的类型
};
对于类型Foo
Foo将成为Bar<Foo> 的友元
模板类型别名
可以 **typedef Blob
1
2
template<typename T> using twin = pair<T,T>;
twin<string> authors;//authors是一个pair<string,string>
类模板的static成员
类模板可以声明static成员
1
2
3
4
5
6
template <typename T> class Foo{
public:
static size_t count(){return ctr;}
private:
static size_t ctr;
};
Foo<x>
都有一个ctr和count成员,也即都共享相同的ctr对象和count函数。 类模板的每个实例都有一个独有的static对象 一个static成员只有在使用时才会实例化
16.1.3 模板参数
模板参数与作用域
模板参数会隐藏外层作用域中声明的相同名字。
模板声明
模板声明必须包含模板参数
与函数参数相同,声明中的模板参数的名字不必与定义中相同
使用类的类型成员
默认情况下,cpp成员假定通过作用运算符访问的名字不是类型。因此我们希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是一个类型。 template <typename T> typename T::value_type top(const T& c)
当我们希望通知编译器一个名字表示类型时,必须使用关键字typename,而不能使用class.
默认模板实参
模板默认实参与类模板
16.1.4 成员模板
一个类(无论是普通类还是类模板)可以包含本身时模板的成员函数 这种成员被称为成员模板不能是虚函数
普通(非模板)类的成员模板
类模板的成员模板
实例化与成员模板
16.1.5 控制实例化
当模板被使用时才会实例化,意味着相同的实例可能出现在多个对象文件中 当两个或多个独立编译的源文件使用了相同的模板,并提供了相同的模板参数时,每个文件中都会有该模板的一个实例。 大系统中,多个文件中实例化相同模板的额外开销很严重。 新标准中,可以通过显式实例化在避免 有以下形式:
1
2
extern template declaration;//实例化声明
template declaration;//实例化定义
declaration 是一个类或者函数声明,其中所有模板参数已经被替换为模板实参
实例化定义会实例化所有成员
在一个类模板的实例化定义中,所用类型必须能用于模板的所有成员函数
16.1.6 效率与灵活性
shared_ptr和unique_ptr之间明显不同是它们所保存的指针的策略 前者给予我们共享指针所有权的能力 后者则独占指针 前者可以重载产出其 后者必须在定义unique_ptr时以显式模板实参的形式提供删除器的类型。
在运行时绑定删除器
删除器必须保存为一个指针或一个封装了指针的类