当前位置:嗨网首页>书籍在线阅读

21-模板的具体化

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

14.4.6 模板的具体化

类模板与函数模板很相似,因为可以有隐式实例化、显式实例化和显式具体化,它们统称为具体化(specialization)。模板以泛型的方式描述类,而具体化是使用具体的类型生成类声明。

1.隐式实例化

到目前为止,本章所有的模板示例使用的都是隐式实例化(implicit instantiation),即它们声明一个或多个对象,指出所需的类型,而编译器使用通用模板提供的处方生成具体的类定义:

ArrayTP<int, 100> stuff; // implicit instantiation

编译器在需要对象之前,不会生成类的隐式实例化:

ArrayTP<double, 30> * pt;      // a pointer, no object needed yet
pt = new ArrayTP<double, 30>; // now an object is needed

第二条语句导致编译器生成类定义,并根据该定义创建一个对象。

2.显式实例化

当使用关键字template并指出所需类型来声明类时,编译器将生成类声明的显式实例化(explicit instantiation)。声明必须位于模板定义所在的名称空间中。例如,下面的声明将ArrayTP<string, 100>声明为一个类:

template class ArrayTP<string, 100>; // generate ArrayTP<string, 100> class

在这种情况下,虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)。和隐式实例化一样,也将根据通用模板来生成具体化。

3.显式具体化

显式具体化(explicit specialization)是特定类型(用于替换模板中的泛型)的定义。有时候,可能需要在为特殊类型实例化时,对模板进行修改,使其行为不同。在这种情况下,可以创建显式具体化。例如,假设已经为用于表示排序后数组的类(元素在加入时被排序)定义了一个模板:

template <typename T>
class SortedArray
{
     ...// details omitted
};

另外,假设模板使用>运算符来对值进行比较。对于数字,这管用;如果T表示一种类,则只要定义了T::operator>()方法,这也管用;但如果T是由const char *表示的字符串,这将不管用。实际上,模板倒是可以正常工作,但字符串将按地址(按照字母顺序)排序。这要求类定义使用strcmp(),而不是>来对值进行比较。在这种情况下,可以提供一个显式模板具体化,这将采用为具体类型定义的模板,而不是为泛型定义的模板。当具体化模板和通用模板都与实例化请求匹配时,编译器将使用具体化版本。

具体化类模板定义的格式如下:

template <> class Classname<specialized-type-name> { ... };

早期的编译器可能只能识别早期的格式,这种格式不包括前缀template<>:

class Classname<specialized-type-name> { ... };

要使用新的表示法提供一个专供const char *类型使用的SortedArray模板,可以使用类似于下面的代码:

template <> class SortedArray<const char char *>
{
     ...// details omitted
};

其中的实现代码将使用strcmp()(而不是>)来比较数组值。现在,当请求const char *类型的SortedArray模板时,编译器将使用上述专用的定义,而不是通用的模板定义:

SortedArray<int> scores;         // use general definition
SortedArray<const char *> dates; // use specialized definition

4.部分具体化

C++还允许部分具体化(partial specialization),即部分限制模板的通用性。例如,部分具体化可以给类型参数之一指定具体的类型:

// general template
    template <class T1, class T2> class Pair {...};
// specialization with T2 set to int
    template <class T1> class Pair<T1, int> {...};

关键字template后面的<>声明的是没有被具体化的类型参数。因此,上述第二个声明将T2具体化为int,但T1保持不变。注意,如果指定所有的类型,则<>内将为空,这将导致显式具体化:

// specialization with T1 and T2 set to int
    template <> class Pair<int, int> {...};

如果有多个模板可供选择,编译器将使用具体化程度最高的模板。给定上述三个模板,情况如下:

Pair<double, double> p1; // use general Pair template
Pair<double, int> p2;    // use Pair<T1, int> partial specialization
Pair<int, int> p3;       // use Pair<int, int> explicit specialization

也可以通过为指针提供特殊版本来部分具体化现有的模板:

template<class T>    // general version
class Feeb { ... };
template<class T*>   // pointer partial specialization
class Feeb { ... };  // modified code

如果提供的类型不是指针,则编译器将使用通用版本;如果提供的是指针,则编译器将使用指针具体化版本:

Feeb<char> fb1;   // use general Feeb template, T is char
Feeb<char *> fb2; // use Feeb T* specialization, T is char

如果没有进行部分具体化,则第二个声明将使用通用模板,将T转换为char *类型。如果进行了部分具体化,则第二个声明将使用具体化模板,将T转换为char。

部分具体化特性使得能够设置各种限制。例如,可以这样做:

// general template
    template <class T1, class T2, class T3> class Trio{...};
// specialization with T3 set to T2
    template <class T1, class T2> class Trio<T1, T2, T2> {...};
// specialization with T3 and T2 set to T1*
    template <class T1> class Trio<T1, T1*, T1*> {...};

给定上述声明,编译器将作出如下选择:

Trio<int, short, char *> t1; // use general template
Trio<int, short> t2; // use Trio<T1, T2, T2>
Trio<char, char *, char *> t3; use Trio<T1, T1*, T1*>