模板是泛型编程的基础
函数模板
模板定义以关键字template
开始,后跟一个模板参数列表。这是一个逗号分隔的一个或多个模板参数的列表。用尖括号包围起来。
模板函数定义的一般形式:
template <class type> ret-tye func-name(parameter list)
{}
模板类型参数前必须使用关键字typename
或者class
在这里typename
可以代替class
,两者等价。
模板参数列表的作用很像函数参数列表
当我们调用一个函数模板时,编译器通常用函数实参来为我们推断模板实参。然后用推断出的模板参数为我们实例化一个特定版本的函数。
除了定义类型参数,我们还可以在模板中定义非类型参数。一个非类型参数表示一个值而非一个类型。当一个模板被实例化时,非类型参数被一个用户提供的或编译器推断出的值(常量表达式)所代替,从而允许编译器再编译时实例化模板
例如:我们想要比较两个字符串常量的大小,由于我们希望能够比较不同长度的字符串常量,所以在定义的时候使用了非类型的参数。
template<unsigned N,unsigned M>
int compare(const char (&p1)[N],const char (&p1)[M])
{return strcmp(p1,p2);
}//调用
compare("123","789456");
则编译后编译器会实例化如下函数:
int compare(const char (&p1)[4],const char(&p2)[7])
{return strcmp(p1,p2);
}
一个非类型参数可以是一个整型或者是一个指向对象或函数类型的指针或者引用,绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或者引用非类型参数的实参必须具有静态的生存期。我们不能用一个普通(非static
)局部变量或者动态对象作为指针或引用非类型模板参数的实参。(虽然我也不是很懂。。。从书上抄来的)
类模板
与函数模板不同之处是编译器不能为类模板推断模板参数类型。为了使用类模板,我们必须在模板名后的尖括号中提供额外信息。
类模板:
template <class type> class class-name
{};
编译器使用显式模板实参来实例化出特定的类
每个实例形成了独立的类。互相之间没有关系。
类模板的成员函数具有和模板相同的模板参数。因此定义在类模板之外的成员函数必须以关键字template
开始,后接类模板参数列表。而且,我们还要说明成员属于哪个类,而模板生成的类必须包含模板实参。当我们定义一个成员函数时,模板实参与模板形参形同。例如:
template <typename T>
ret-type class-name<T>::number-name(parm-list)
{}