14.1 模板的参数
1. 模板参数的语法是:
模板参数:
类型参数
参数声明
类型参数:
class 标识符 opt
class 标识符 opt = 类型 id
typename 标识符 opt
typename 标识符 opt = 类型 id
template < 模板参数列表 > class 标识符 opt
template < 模板参数列表 > class 标识符 opt = id 表达式
2. 在模板参数中, class 与 typename 关键字没有语义上的区别。 typename 后面跟一个未限定的 id 命名了一个模板类型参数。 typename 后面跟一个限定的 id 表示非类型参数声明中的类型。模板参数声明中不能指定存储类型。【注:模板参数有可能是类模板,例如:
template<class T> class myarray { /* ... */ };
template<class K, class V, template<class T> class C = myarray>
class Map {
C<K> key;
C<V> value;
// ...
}; 】
3. 模板声明的作用域中,类型参数将标识符定义为一个类型名(如果用 class 或 typename 关键字声明),或者将其定义为一个模板名(如果用 template 关键字声明)。【注:按照名字查找规则,模板参数既可以解释为非类型模板参数,又可以解释为类型参数(其标识符是已经存在的一个类的名字),这种情况下,模板参数总是被解释为类型参数。例如:
class T { /* ... */ };
int i;
template<class T, T i> void f(T t)
{
T t1 = i; // 参数类型 T 和 i
::T t2 = ::i; // 全局名字空间中的 T 和 i
}
这里,模板 f 有个类型参数 T ,而非无名的类型为 T 的非类型参数。】
4. 非类型模板参数必须有如下的类型(可以为 cv- 修饰过的):
a) 整型或枚举型
b) 对象指针或函数指针
c) 对象引用或函数引用
d) 成员指针
【按:编译器用只4 字节就能表示的类型】
5. 【注:根据控制模板参数形式的规则,非类型模板参数不允许其他类型,不论是下述的显式形式,还是隐式形式。】在确定模板参数类型的时候,最外层的 cv- 修饰符会被忽略。
6. 非类型的非引用模板参数不是左值。该参数不能被赋值,也不能以其他方式改变其值。非类型的非引用模板参数不能取地址。当非类型的非引用模板参数被用做一个引用的初值时,总是会产生临时对象。
【例:
template<const X& x, int i> void f()
{
i++; // 错误 : 试图改变非类型的非引用模板参数的值
&x; // OK
&i; // 错误 : 试图取非类型的非引用模板参数的地址
int& ri = i; // 错误 : 绑定到临时对象上的是个非 const 引用【按:非 const 可能改变 i 的值】
const int& cri = i; // OK: 绑定到临时对象上的是个 const 引用
}
】
7. 非类型模板参数不能声明为浮点类型、 class 或 void 类型。【例:
template<double d> class X; // error
template<double* pd> class Y; // OK
template<double& rd> class Z; // OK
】
8. 类型为“ T 型数组”或类型为“返回类型为 T 的函数”的非类型模板参数会相应地自动调整为“ T 的指针”或“返回类型为 T 的函数指针”。【例如:
template<int *a> struct R { /* ... */ };
template<int b[5]> struct S { /* ... */ };
int p;
R<&p> w; // OK
S<&p> x; // OK : 类型调整
int v[5];
R<v> y; // OK :隐式实参类型转换
S<v> z; // OK :(对模板 S: S<int[5]> à S<int*> )类型调整和(对 v: int[5] à int* )隐式实参类型转换
】
9. 默认模板参数是模板参数中 = 后面的模板实参部分。默认模板参数可以指定为任意类型的模板参数(类型,非类型,模板)。默认模板参数可以在类模板声明或定义中指定。默认模板参数不能在函数模板声明或定义中指定;也不能在类模板的成员函数定义的模板参数列表中指定。默认模板参数不能在友元模板声明中指定。
10. 可以用作模板声明或定义的默认模板参数的集合,是通过合并其定义(如果在同一个域中)及其在同一个域中的所有声明而得到的;合并的方式与函数的默认参数的合并方式完全相同( 8.3.6 )。【例如:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
等价于:
template<class T1 = int, class T2 = int> class A;
】
11. 如果模板的其中一个参数有默认值,那么后面跟着的所有模板参数都必须有默认值 【按:不论是直接提供还是通过合并得到】 。【例:
template<class T1 = int, class T2> class B; // error
】
12. 模板参数不能在同一个域中的两个不同的声明中同时指定默认参数。 【按:避免默认参数的重定义】【例如:
template<class T = int> class X;
template<class T = int> class X { /*... */ }; // error
】
13. 模板参数的作用范围是:从它的声明点开始,一直到模板结束。由此得出,模板参数可以用于后续模板参数的声明及其默认参数中。【例如:
template<class T, T* p, class U = T> class X { /* ... */ };
template<class T> void f(T* p = new T);
】
14. 模板参数不能用于自己的默认参数中。
15. 当处理一个非类型模板参数的默认参数时,第一个非嵌套的 > 符号被当做模板参数列表的结束符,而不是大于号。【例如:
template<int i = 3 > 4 > // 语法错误
class X { /* ... */ };
template<int i = (3 > 4) > // OK ,虽然有 > ,但是嵌套在 () 中
class Y { /* ... */ };
】