模板非类型(non-type)参数(实参)(argument)
模板参数列表中提供的非类型模板参数是指其值可以在编译时确定的表达式。此类参数必须是常量表达式、具有外部链接的函数或对象的地址,或静态类成员的地址。非类型模板实参通常用于初始化类或指定类成员的大小。(注:所谓非类型是指这个参数并不是一种泛型类型,而是泛型的实例,即直接指定的具体数据类型,在调用时指定的不是类型,而是类型的实例。)
对于非类型整数参数,只要实例实参具有适合参数类型的值和符号,实例参数就会与相应的模板参数匹配。
对于非类型地址参数,实例实参的类型必须是标识符或 &identifier 形式,并且实例实参的类型必须与模板参数完全匹配,但在匹配之前,函数名称会更改为指向函数类型的指针。
模板参数列表中非类型模板参数的结果值构成模板类类型的一部分。如果两个模板类名称具有相同的模板名称,并且它们的参数具有相同的值,则它们是同一个类(哪怕它们的类名称不同)。
在以下示例中,定义了一个类模板,它需要非类型模板 int 参数以及类型参数:
template<class T, int size> class Myfilebuf
{
T* filepos;
static int array[size];
public:
Myfilebuf() { /* ... */ }
~Myfilebuf();
advance(); // 函数在程序中别处定义
};
在此示例中,模板实参 size 成为模板类名称的一部分。此类模板类的对象由该类的类型参数 T 和非类型模板实参 size 的值创建。
可以从该模板创建一个对象 x 及其对应的模板类(带有参数 double 和 size=200),并将值作为其第二个模板参数:
Myfilebuf<double, 200> x;
x 也可以使用算术表达式来创建:
Myfilebuf<double, 10 * 20> x;
这些表达式创建的对象是相同的,因为模板实参按相同方式计算。第一个表达式中的值 200 可以用一个表达式来表示,该表达式在编译时的结果已知等于 200,如第二个构造所示。
注意:
包含 < 符号或 > 符号的参数必须用括号括起来,以防止当任一符号实际上用作关系运算符时被解析为模板参数列表分隔符。例如,以下定义中的参数是有效的:
Myfilebuf <double, (75 > 25)> x; // 有效
但是,以下定义无效,因为大于运算符 ( > ) 被解释为模板参数列表的结束分隔符:
Myfilebuf < double, 75>25 > x; // 错误
如果模板参数的计算结果不相同,则创建的对象将属于不同类型:
Myfilebuf<double, 200> x; // 创建类 Myfilebuf<double,200> 的对象 x
Myfilebuf<double, 200.0> y; //错, 200.0 是一个double型,非 int
y 的实例化失败,因为值 200.0 是 double 类型,而模板参数是 int 类型。
下面的两个对象:
Myfilebuf<double, 128> x
Myfilebuf<double, 512> y
是分别模板特化的对象。稍后使用 Myfilebuf<double> 引用这些对象中的任何一个都是错误的。
仅具有非类型实参的模板类是有效的模板类。例如,以下模板是有效的类模板:
template<int i> class C
{
public:
int k;
C() { k = i; }
};
同样,这两个声明指的是不同的类,因为它们的非类型参数的值类型不同。但下面两个模板类是相同的类:
template<int i> class A
{
public:
A(int i){
int m = i;
}
};
template<int i> class B
{
public:
B(int i) {
int m = i;
}
};