函数模板
可以定义一个通用的函数模板(function template),而不是为每个类型都定义一个新的函数。
template <typename T>
int compare(const T &v1, const T &v2)
{
if(v1<v2) return -1;
if(v2<v1) return 1;
return 0;
}
模板定义以关键字template开始,后跟一个模板参数列表(template parameter list),这是一个以逗号分隔的一个或多个模板参数(template parameter)的列表,用<和>包围起来。
Note:在模板定义中,模板参数列表不能为空
当使用模板的时候,我们(显式或者隐式地)指定模板实参(template argument),将其绑定在模板参数上。
实例化函数模板
编译器通常使用函数实参的类型来确定绑定到模板参数T的类型。例如:
cout << compare(1,0) << endl; //T为int
实参的类型是int,因此编辑器可以推断出模板实参为int,并将它绑定到模板参数T上。
编辑器用推断出的模板参数来为我们实例化(instantiate)一个特定版本的函数。例如,
//实例化出 int compare(const int&, const int&)
cout << compare(1,0) << endl; //T为int
//实例化出 int compare(const vector<int>&, const vector<int>&)
vector<int> vec1(1,2,3), vec2(4,5,6);
cout << compare(vec1, vec2) << endl; //T为vector<int>
这些被编译器生成的版本通常被称为模板的实例(instantiation)
模板类型参数
上文中的compare函数都有一个模板类型参数(type parameter)。我们可以将类型参数看作类型说明符,就像内置类型或者类类型说明符一样使用。类型参数可以用来:
- 指定返回类型
- 指定函数的参数类型
- 在函数体内用于变量声明或类型转换
// ok: same type used for the return type and parameter
template <typename T> T foo(T* p)
{
T temp = *p; //tmp will have the type to which p points
//...
return tmp;
}
类型参数(type parameter)前面必须使用关键字class或者typename
// error: must precede U with either *typename* or *class*
template <typename T, U> T calc(const T&, const U&);
在模板参数列表中,这两个关键字的含义相同,可以互换使用。一个模板参数列表中可以使用这两个关键字。
// ok: no distinction between *typename* and *class* in a template parameter list
template <typename T, class U> calc (const T&, const U&);
非类型模板参数
除了定义类型参数(type parameter),还可以在模板中定义非类型参数(nontype parameter)。一个非类型参数表示一个值而非类型参数。通过一个特定的类型名而不是关键字class或template来指定非类型参数。
当一个模板被实例化时,非类型参数被用户提供的或编译器推断出来的值所替代。
Note:这些值必须是常量表达式
例子:编写一个compare版本处理字符串字面常量(string literals),这种字面常量是const char的数组。由于不能拷贝一个数组,因此将参数定义为数组的引用[注意区分引用的数组以及数组的引用]。并且我们希望能够比较不同长度的字符串字面常量,因此模板定义了两个非类型参数(nontype parameter)。两个模板参数分别代表了两个数组的长度。
Reference:
template<unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M])
{
return strcmp(p1, p2);
}
当调用这个版本的compare时:
compare("hi", "mom");
编译器会使用字面常量的大小来代替N和M,从而实例化模板。编译器会实例化出如下版本:
int compare(const char (&p1)[3], const char (&p2)[4]); //the compiler inserts a null terminator at the end of a string literal