14.1 模板形参
1、template-parameter的语法是:
template-parameter:
type-parameter
parameter-declaration
type-parameter:
class identifieropt
class identifieropt = type-id
typename identifieropt
typename identifieropt = type-id
template < template-parameter-list > class identifieropt
template < template-parameter-list > class identifieropt = id-expression
2、在一个template-parameter中,class和typename之间没有语义上的不同。typename后面跟着一个unqualified-id命名了一个模板类型形参。typename后面跟着一个qualified-id指示在一个非类型parameter-declaration中的类型[注:由于模板的template-parameter以及模板的template-argument被对待为说明性目的的类型,因此术语非类型形参以及非类型实参被用于引用为非类型、非模板形参和实参]。一个存储类不应该在一个template-parameter声明中指定。[注:一个模板形参可以是一个类模板。比如:
template <class T> class myarray {/* ... */ };
template <class K, class V, template <class T> class C = myarray>
class Map {
C<K> key;
C<V> value;
};
——注结束]。
[译者注:对于这里的模板类型形参和非类型形参,我将举一个例子来说明:
struct A
{
typedef int TYPE;
};
// T is unqualified, T::TYPE is qualified
template <typename T, typename T::TYPE>
struct B
{
};
extern "C" void cpp_test(void)
{
B<A, 100>();
}
]
3、 在模板声明的作用域中,一个type-parameter定义了其identifier是一个type-name(如果用class或typename声明)或template-name(如果用template声明)。[注:因为名字查找规则,一个可以被解释为一个非类型template-parameter或一个type-parameter(因为其identifier是一个已经存在的类的名字)的template-parameter被采取为一个type-parameter。比如:
class T { /* ... */ };
int i;
template <class T, T i> void f(T t)
{
T t1 = i; // template-parameter T以及i
::T t2 = ::i; // 全局名字空间成员T以及i
}
这里,模板f具有一个称为T的type-parameter,而不是一个未命名的非类型的类T的template-parameter。]
4、 一个非类型的template-parameter应该具有下列(可选地cv限定)类型:
——整型或枚举类型,
——指向对象的指针或指向函数的指针,
——对对象的引用或对函数的引用,
——指向成员的指针。
5、[注:其它类型是被禁止的,要么是下面显式地禁止,要么是被支配template-argument的形式的规则隐式地禁止(14.3)。]template-parameter上的顶层cv-qualifier被忽略,当判定其类型时。[译者注:
// 这里,const是顶层,因此被忽略
template <int * const P>
void funcA(void)
{
++*P;
}
// 这里,const不是顶层,因此不能被忽略
template <int const *P>
void funcB(void)
{
}
int a = 10;
extern "C" void cpp_test(void)
{
funcA<&a>();
funcB<&a>();
}
]
6、一个非类型非引用的template-parameter并不是一个左值。它不应该被赋值也不能在任何其它方式下让它的值受到更改。一个非类型非引用的template-parameter不能将其地址被获取。当一个非类型非引用的template-parameter被用作为一个引用的初始化器时,总要使用一个临时变量。[例如:
template <const X& x, int i> void f()
{
i++; // error: 对模板形参值的修改
&x; // OK
&i; // error: 非引用模板形参的地址
int& ri = i; // error: 非const引用绑定到临时变量
const int& cri = i; // OK: const引用绑定到临时变量
}
——例结束]
7、一个非类型的template-parameter不应该被声明具有浮点、类、或void类型。[例:
template <double d> class X; // error
template <double* pd> class Y; // OK
template <double& rd> class Z; // OK
——例结束]
8、一个“T的数组”或“返回T的函数”类型的非类型template-parameter被分别调整为“指向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 由于模板形参调整以及隐式的实参转换
——例结束]
9、一个默认的template-parameter是在一个template-parameter中的=之后的一个template-argument(14.3)。一个默认的template-argument可以对任何一种template-parameter(类型,非类型,模板)被指定。一个默认的template-argument可以在一个类模板声明或是一个类模板定义中被指定。一个默认的template-argument不应该在一个函数模板声明或一个函数模板定义中被指定,也不该在一个类模板的一个成员定义的template-parameter-list中被指定。一个默认的template-argument也不该在一个友元模板声明中被指定。[译者注:比如:
template <typename T = int> // OK
struct A
{
template <typename Q = T> // OK in GCC
struct B
{
};
template <typename R = int> // OK in GCC
friend class C;
private:
T var;
};
template <typename T>
class C
{
private:
T var;
public:
C(const A<T>& a)
{
var = a.var;
}
};
template <typename T = int> // error: 函数模板中不能使用默认模板实参
void func(void)
{
}
extern "C" void cpp_test(void)
{
A<>::B<>();
C<>(A<>());
}
]
10、与一个模板声明或定义一起可使用的默认的template-argument集合,通过将根据定义的默认实参(如果在作用域中)与作用域中的所有声明,如同默认函数实参相同的方式(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-parameter具有一个默认的template-argument,那么所有后面的template-parameter应该提供一个默认的template-argument。[例:
template <class T1 = int, class T2> class B; // error
——例结束]
12、一个template-parameter不应该通过在同一作用域中的两个不同的声明来给予默认实参。[例:
template <class T = int> class X;
template <class T = int> class X { /* ... */ }; // error
——例结束]
13、一个template-parameter的作用域从其声明点一直延伸到其模板的结束。特别地,一个template-parameter可以被用在后续的template-parameter以及其默认的实参的声明中。[例:
template <class T, T* p, class U = T> class X { /* ... */ };
template <class T> void f(T* p = new T);
——例结束]
14、一个template-parameter不应该被用在其自己默认的实参中。[译者注:比如:
template <class T>
struct A
{
typedef T TYPE;
};
template <typename T, typename Q = class A<T>::TYPE> // OK
struct C { };
template <typename T = class A<T>::TYPE> // error
struct D { };
]
15、当为一个非类型的template-parameter解析一个默认的template-argument时,第一个非嵌套的>被采取为的template-parameter-list结束,而不是一个大于操作符。[例:
template <int i = 3 > 4 > // 语法错误
class X { /* ... };
template <int i = (3 > 4) > // OK
class Y { /* ... */ };
——例结束]