类模板的定义和声明都以关键字template开头。关键字后面是一个用逗号分隔的模板参 数表,用尖括号(<>)括起来,这个表被称为类模板的模板参数表。它不能为空,模板参数可以是一个类型参数,也可以是一个非类型参数。如果是非类型参数,则代表一个常量表达式。模板的类型参数由关键字class或关键字typename及其后的标识符构成。在模板参数表中,关键字class和typename的意义相同。在标准 C++之前,关键字typename没有被支持,把这个关键字加入到C++中是因为有时必须要靠它来指导编译器解释模板定义。这两个关键字表明后面的参数名代表一个内置的或用户定义的类型。任何内置的或用户定义的类型,如 int、char*、complex或string都是T的有效实参。一个类模板可以有多个类型参数:
template <class T1, class T2, class T3>
但是,每个模板类型参数的前面都必须有关键宇class或 typename。一旦声明了类型参数,那么在类模板定义的余下部分中,它就可以被用作类型指示符。它在类模板中的使用方式与内置的或用户定义的类型在非模板类定义中的用法一样。
模板非类型参数由一个普通参数声明构成。一个非类型参数指示该参数代表了一个潜在的值,而这个值又代表类模板定义中的一个常量。例如,一个Buffer类模板可以有一个类型参数来表示它所包含的元素类型,和一个非类型参数来表示其大小的常量值。例如:
template <class Type, int size>
class Buffer;
一个类定义或声明紧跟在模板参数表后面。除了模板参数外,类模板的定义看起来和非 模板类相同。在程序中Type会被各种内置的和用户定义的类型代替,类型替换的过程被称为模板实例化。
模板参数的名字,在它被声明为模板参数后,一直到模板声明或定义的结束,都可以被使用。如果在全局域中声明了与模板参数同名的变量,则该变量被隐藏掉。当然,模板参数名不能被用作在类模板定义中声明的类成员的名字。
模板参数的名字在模板参数表中只能被引入一次。例如,否则将被标记为编译时刻:
// 错误: 重复使用名为 Type 的模板参数
template <class Type, class Type>
class container;
在不同的类模板声明或定义之间,模板参数的名字可以被重复使用。
// ok: 名字 'Type' 在不同模板之间可被重复使用
template <class Type>
class QueueItem;
template <class Type>
class Queue;
在类模板的前向声明和类模板定义中,模板参数的名字可以不同。例如,下面三个QueueItem都引用同一个类模板。
// 所有三个 QueueItem 声明都引用同一个类模板
// 模板的声明
template <class T> class QueueItem;
template <class U> class QueueItem;
// 模板的真正定义
template <class Type>
class QueueItem { ... };
类模板的参数可以有缺省实参。这对类型参数和非类型参数都一样,像函数参数的缺省实参一样。模板参数的缺省实参是一个类型或值,当模板被实例化时,如果没有指定实参,则使用该类型或者值,缺省实参应该是一个“对类模板实例的多数情况都适合”的类型或值。 在下面的例子中,如果模板实例的名字没有指定Buffer 的大小。则实例化的Buffer的大小是1024个项:
template <class Type, int size = 1024>
class Buffer;
类模板的后续声明可以为模板参数提供附加的缺省实参,正如函数参数的缺省实参的情形一样,在向左边的参数提供缺省实参之前,必须首先给最右边未初始化的参数提供缺省实参。例如:
template <class Type, int size = 1024>
class Buffer;
// ok: 考虑两个声明中的缺省实参
template <class Type = string , int size>
class Buffer;
在类模板定义中,类模板的名字可以被用作一个类型指示符。凡是可以使用非模板类名的地方都可以用它。例如,下面是一个比较完整的Queueltem类模板的版本:
template <class Type>
class QueueItem
{
public:
QueueItem( const Type & );
private:
Type item;
QueueItem *next;
};
注意:在类模板定义中,QueueItem类模板名的每次出现都是QueueItem<Type>的缩写。这种简写形式只能被用在类模板QueueIte自己的定义中,以及在类模板定义之外出现的成员定义中。当QueueItem在其他模板定义中被用作一个类型指示符时,我们必须指定完整的模板参数表。在下面的例子中,类模板被用在函数模板display的定义中,在这种情况下,类模板QueueItem的名字必须跟有模板参数,就像在QueueItem<Type>中一样:
template <class Type>
void display( QueueItem<Type> &qi )
{
QueueItem<Type> *pqi = &qi;
// ...
}
C++ Primer