一个特例化版本就是模板的一个独立的定义,在其中一个或多个模板参数被指定为特定的类型。
定义函数模板特例化
当我们特例化一个函数模板时,必须为原模板中的每个模板参数都提供实参。为了指出我们正在实例化一个模板,应使用关键字template后跟一个空尖括号对(<>)。空尖括号指出我们将为原模板的所有模板参数提供实参:
// compare的特殊版本,处理字符数组的指针
template <>
int compare(const char* const& p1, const char* const& p2)
{
return strcmp(p1, p2);
}
当我们定义一个特例化版本时,函数参数类型必须与一个先前声明的模板中对应的类型匹配。本例中我们特例化:
template <typename T> int compare(const T&, const T&);
我们希望定义此函数的一个特例化版本,其中T为const char*。我们的函数要求一个指向此类型const版本的引用。一个指针类型的const版本是一个常量指针而不是指向const类型的指针。我们需要在特例化版本中使用的类型是const char* const &,即一个指向const char的const指针的引用。
函数重载与模板特例化
当定义函数模板的特例化版本时,我们本质上接管了编译器的工作。即,我们为原模板的一个特殊实例提供了定义。重要的是要弄清:一个特例化版本本质上是一个实例,而非函数名的一个重载版本。
为了特例化一个模板,原模板的声明必须在作用域中。而且,在任何使用模板实例的代码之前,特例化版本的声明也必须在作用域中。
如果一个程序使用一个特例化版本,而同时原模板的一个实例具有相同的模板实参集合,就会产生错误。
模板及其特例化版本应该声明在同一个头文件中。所有同名模板的声明应该放在前面,然后是这些模板的特例化版本。
类模板特例化
默认情况下,无序容器使用hash< key_type >来组织其元素。为了让我们自己的数据类型也能使用这种默认组织方式,必须定义hash模板的一个特例化版本。一个特例化hash类型必须定义:
1。一个重载的调用运算符,它接受一个容器关键字类型的对象,返回一个size_t。
2。两个类型成员,result_type和argument_type,分别调用运算符的返回类型和参数类型。
3。默认构造函数和拷贝赋值运算符。
// 打开std命名空间,以便特例化std::hash
namespace std
{
// 我们正在定义一个特例化版本,模板参数为SalesData
template <>
struct hash<SalesData>
{
// 用来散列一个无序容器的类型必须要定义下列类型
typedef size_t result_type;
// 默认情况下,此类型需要==
typedef SalesData argument_type;
size_t operator()(const SalesData& s) const;
// 我们的类使用合成的拷贝控制成员和默认构造函数
};
size_t
hash<SalesData>::operator()(const SalesData& s) const
{
return hash<string>()(s.bookNo) ^
hash<unsigned>()(s.units_sold) ^
hash<double>()(s.revenue);
}
}
类模板部分特例化
与函数模板不同,类模板的特例化不必为所有模板提供实参。我们可以只指定一部分而非所有模板参数,或是参数的一部分而非全部特性。一个类模板的部分特例化(partial specialization)本身是一个模板,使用它时用户还必须为那些在特例化版本中未指定的模板参数提供实参。
部分特例化版本的模板参数列表是原始模板的参数列表的一个子集或者是一个特例化版本。
特例化成员而不是类
我们可以只特例化特定成员而不是特例化整个模板。