模板与泛型编程
函数模板
我们可以定义一个通用的函数模板(function tempalte),而不是为每个类型都定义一个新函数。一个函数模板就是一个公式,可以用来生成针对类型的函数版本,例如以下的compare函数:
template <typename T>
int compare(const T &v1,const T &v2)
{
if(v1 < v2) return -1;
if(v1 > v2) return 1;
}.
模板定义从template关键字开始,后面跟一个模板参数列表。用T表示一个类型,T的实际类型在编译时根据compare的实际使用情况来确定。
非类型模板参数
非类型参数表示一个值而非一个类型
template<unsigned N,unsigned M>
int compare(const char (&p1)[N],const char (&p2)[M])
{
return strcom(p1,p2);
}
compare("hi","mom");
int compare(const char (&p1)[3],const char (&p2)[4]);
//在compare函数调用这个版本时,编译器会用字面值大小来代替N和M,从而实例化模板。
//编译器会在一个字符串常量的末尾插入一个空字符串作为终结符,所以编译器在此例中会初始化一下模板。
非类型模板参数的模板实参必须是常量表达式。
模板程序应该尽量减少对实参类型的要求。
函数模版和类模板成员函数的定义通常放在头文件中,需要保证传递给模板的实参支持所要求的操作,以及这些操作在模板中能正确的工作
类模板
类模板的名字不是一个类类型名,类模板用来实例化类型,一个实例化类型总是包含模板参数的
一个类模板的每个实例都形成一个独立的类。类型Blob<string>与任何其他的Blob类型都没有关联,也不会对其他任何的Blob类型的成员友特殊的访问权限。
定义在类模板外的成员函数都要从模板参数开始
template <template T>
Blob<T>::Blob():data (std::make_shared<ste::vector<iT>>()){}
//该段代码在Blob<T>作用域中定义了名为Blob的成员函数,类似Blob的默认构造函数
//此函数分配一个空Vector,并将指向Vector,并将指向vector的指针保存在data中
在一个类模板的作用域内,可以直接使用模板名而不必指定模板实参。
类模板和友元
template <typename> class Blobptr;
template <typename> class Blob;
template <tymename T>
bool operator == (const Blob<T>&,const Blob<T>&);
template <template T> class Blob
{
friend class BlobPtr<T>;
friend class operator==<T>;
(const Blob<T>&,const Blob<T>&)
};
首先将Blob、BlobPtr和operator==声明为模板。
友元的声明用Blob的模板形参作为它们自己的模板实参。友好关系被限定在用相同类型实例化的Blob和BlobPtr相等运算符之间
为了让所有的实例都成为友元,友元声明中必须使用与类模板本身不同的模板参数。
令模板自己的类型参数声明为友元。
template <typename Type> class Bar
{
friend Type;//将访问权限授予用来实例化Bar的类型
//...
};
模板类型别名
新标准允许为类模板定义一个类型别名
template<typename T> using twin = pair<T,T>;
twin<string> authors; //authors是一个pair<string,string>
//将twin定义为成员类型相同的pair的类型别名。
类模板的static成员
template <typename T> class Foo
{
public:
static std::size_t count() { return ctr;}
private:
static std::size_t ctr;
};
//所有的Foo类型的对象共享相同的ctr对象和count函数
模板参数
模板参数遵循普通的作用域规则。一个模板参数名的可用范围是在其声明之后,至模板声明或定义之前。模板参数会隐藏外层作用域中声明的相同的名字,在模板内不能重用模板参数名。
typedef double A;
template <typename A,typename B> void f(A a,B b)
{
A tmp = a; //tmp的类型模板参数A的类型,而非double
double B; //错误:重声明模板参数B
}
使用类的类型成员
template <typename T>
typename T::value_type top(const T& c)
{
if(!c.empty())
return c.back();
else
return typename T::value_type();
}
如果要使用一个模板类型参数的类型参数,就必须显示告诉编译器改名字是一个类型
使用关键字typename来实现
typename与class之间的区别
成员模板
一个类(无论是普通类还是类模板)可以包含本身是模板的成员函数。这种成员函数被称为成员模板