ISO/IEC14882:2003之条款14.3——模板实参
14.3 模板实参
1、有三种形式的template-argument,相应于三种形式的template-parameter:类型,非类型和模板。在一个template-id中所指定的每个template-argument的类型和形式应该匹配于为由在其template-parameter-list中的模板所声明的相应的形参而指定的类型和形式。[例
template<class T> class Array {
T* v;
int sz;
public:
explicit Array(int);
T& operator[](int);
T& elem(int i) { return v[i]; }
// ...
};
Array<int> v1(20);
typedef complex<double> dcomplex; // complex是一个标准库模板
Array<dcomplex> v2(30);
Array<dcomplex> v3(40);
void bar() {
v1[3] = 7;
v2[3] = v3.elem(4) = dcomplex(7, 8);
}
——例结束]
2、在一个template-argument中,一个type-id和一个表达式之间的歧义性被解决为一个type-id,而不管相应的template-parameter的形式。【注:在一个默认的template-argument中没有这样的歧义性,因为template-parameter的形式确定了template-argument可允许的形式。】【例:
template<class T> void f();
template<int I> void f();
void g()
{
f<int()>(); // int()这里作为一个type-id:调用第一个f()
}
——例结束】
3、一个template-argument的名字在它被用作为一个template-argument的点上是可被访问的。【注:如果一个template-argument的名字在它被用作为一个template-argument的点上是可被访问的,那么在相应的template-parameter名所使用的结果实例中没有进一步的访问限制。】【例:
template <class T> class X {
static T t;
};
class Y {
private:
struct S { /* ... */ };
X<S> x; // OK: S是可被访问的
// X<Y::S>有类型Y::S的一个静态成员
// OK: 即使Y::S是私有的
};
X<Y::S> y; // error: S不可访问
——例结束】对于一个类类型的template-argument,模板定义对于模板实参类型的不可访问的成员没有特殊的访问权限。
4、当默认的template-argument被使用时,一个template-argument列表可以为空。在那情况下,空的<>尖括号仍然应该被用作为template-argument-list。【例:
template <class T = char> class String;
String<>* p; // OK: String<char>
String* q; // 语法错误
——例结束】
5、对具有一个类模板特化的一个类型的一个对象的一个显式的析构器调用(12.4),可以显式地指定template-argument。【例:
template<class T> struct A {
~A();
};
void f(A<int>* p, A<int>* q) {
p->A<int>::~A(); // OK: 析构器调用
q->A<int>::~A<int>(); // OK: 析构器调用
}
——例结束】
6、如果一个template-argument的使用引起了在一个模板特化的实例化中有一个不良形式的构造,那么该程序是不良形式的。【译者注:比如,
template <typename T>
void f(T a)
{
int b = a;
}
int main(void)
{
int var = 0;
void *p = &var;
f(var); // OK
f(p); // 不良形式的
}
】
7、当在一个template-id中的模板是一个被重载的函数模板时,在重载集中的非模板函数以及在重载集中的函数模板,对于template-argument不匹配于template-parameter的,全都忽略。如果没有函数模板匹配template-parameter,那么程序是不良形式的。
14.3.1 模板类型实参
1、对应于一个template-parameter(它是一个类型)的一个template-argument应该是一个type-id。
2、一个局部类型,一个不带连接的类型,一个未命名的类型,或来自任一这些类型的一个复合类型不应该被用作为一个模板type-parameter的一个template-argument。【例:
template <class T> class X { /* ... */ };
void f()
{
struct S { /* ... */ };
X<S> x3; // error: 局部类型被用作为template-argument
X<S*> x4; // error: 指向局部类型的指针被用作为template-argument
}
——例结束】【注:一个模板类型实参可以是一个不完整类型(3.9)。】
3、如果一个声明通过依赖于一个template-parameter的一个类型来获得一个函数类型,并且这导致了一个声明并没有使用一个函数声明符的语法形式而具有了函数类型,那么这个程序是不良形式的。【例:
template<class T> struct A {
static T t;
};
typedef int function();
A<function> a; // 不良形式的:将声明A<function>::t作为一个静态成员函数
——例结束】
14.3.2 模板非类型实参
1、对于一个非类型、非模板的template-parameter的一个template-argument应该是下列其中之一:
——一个整型或枚举类型的整型constant-expression[译者注:
enum Color
{
RED, GREEN, BLUE
};
template <enum Color C>
void funcColor(void)
{
}
template <size_t N>
void funcSize(void)
{
}
extern "C" void cpp_test(void)
{
funcColor<RED>(); // OK: RED枚举符是常量表达式
funcSize<sizeof(int[4])>(); // OK: sizeof(int[4])是常量表达式
}
];或
——一个非类型的template-parameter的名字[译者注:
template <unsigned N>
struct A
{
void func(void)
{
A<N>(); // OK
}
};
];或
——带有外部连接的一个对象或函数的地址,包括函数模板和函数template-id,但不包括被表达为& id-expression的非静态类成员,这里,&是可选的,如果这个名字引用了一个函数或数组,或者如果相应的template-parameter是一个引用[译者注:
template <void (*PFUNC)(void)>
struct A
{
void func(void)
{
(*PFUNC)();
}
};
static void s_func(void)
{
}
void g_func(void)
{
}
extern "C" void cpp_test(void)
{
A<&g_func>().func(); // OK
A<&s_func>().func(); // error: s_func不具有外部连接
}
];或
——如5.3.1中所表达的一个指向成员的指针。[译者注:
struct S
{
int a;
void func(void)
{
}
};
template <int S::*MP>
void f1(void)
{
S s;
s.*MP = 100;
}
template <void (S::*MP)(void)>
void f2(void)
{
S s;
(s.*MP)();
}
extern "C" void cpp_test(void)
{
f1<&S::a>(); // OK
f2<&S::func>(); // OK
}
]
2、【注:一个字符串字面量(2.13.4)不满足这些类别的任意一种,从而并不是一个可接受的template-argument。【例:
template<class T, char* p> class X {
// ...
X();
X(const char* q) { /* ... */ }
};
X<int, "Studebaker"> x1; // error: 字符串字面量作为模板实参
char p[] = "Vivisectionist";
X<int, p> x2; // OK
——例结束】——注结束】
3、【注:数组元素和名字的地址或非静态类成员的地址是不可被接受的template-argument。【例:
1 template<int* p> class X { };
2
3 int a[10];
4 struct S { int m; static int s; } s;
5
6 X<&a[2]> x3; // error: 数组元素的地址
7 X<&s.m> x4; // error: 非静态成员的地址
8 X<&S.s> x5; // error: 必须使用&S::s
9 X<&S::s> x6; // OK: 静态成员的地址
——例结束】——注结束】
4、【注:临时变量,未命名左值,以及不具有外部连接的命名左值是不可被接受的template-argument,当相应的template-parameter具有引用类型时。【例:
template<const int& CRI> struct B { /* ... */ }; B<1> b2; // error: 模板实参将会需要临时变量 int c = 1; B<c> b1; // OK
——例结束】——注结束】
5、以下转换被执行在被用作为一个非类型的template-argument的每个表达式上。如果一个非类型的template-argument不能被转换为相应的template-parameter的类型,那么程序是不良形式的。
——对于整型或枚举类型的一个非类型template-parameter,整型晋升(4.5)以及整型转换(4.7)被应用。[译者注:
template <int N> void f1(void) { } template <char C> void f2(void) { } const int i = 100; const char c = 'c'; extern "C" void cpp_test(void) { f1<'c'>(); // OK: integer promotion f2<100>(); // OK: integer conversion f1<c>(); // OK: integer promotion f2<i>(); // OK: integer conversion }
]
——对于指向对象指针类型的一个非类型template-parameter,限定转换(4.4)以及数组到指针的转换(4.2)被应用。[注:特别地,空指针转换(4.10)和对基的派生的转换(4.10)都不被应用。尽管0对于整型类型的一个非类型的template-parameter是一个有效的template-argument,但它对于指针类型的一个非类型的template-parameter并不是一个有效的template-argument。][译者注:
struct A { }; struct B : public A { }; template <void *P> void func(void) { } template <B* P> void f1(void) { } template <A* P> void f2(void) { } A a; B b; extern "C" void cpp_test(void) { func<(void*)NULL>(); // error f1<(B*)&a>(); // error f2<(A*)&b>(); // error f1<&b>(); // OK f2<&a>(); // OK }
]
——对于一个指向对象引用类型的一个非类型template-parameter,没有转换应用。被引用所引用的类型可以比template-argument的类型添加更多或相同的cv限定符。template-parameter直接被绑定到template-argument,而此template-argument必须是一个左值。【译者注:
template <const int& R> void func(void) { } int a = 100; int main (int, char**) { func<a>(); // OK func<(int)&a>(); // error: &a并非左值 }
】
——对于指向函数指针类型的一个非类型template-parameter,只有函数到指针的转换(4.3)被应用。如果template-argument呈现出一组被重载的函数(或指向这些函数的指针),那么匹配函数从该集合中被选择(13.4)。
——对于指向函数引用类型的一个非类型template-parameter,没有转换被应用。如果template-argument呈现出一组被重载的函数,那么匹配函数从该集合中被选择(13.4)。
——对于指向成员函数指针类型的一个非类型template-parameter,没有转换被应用。如果template-argument呈现出一组被重载的函数,那么匹配函数从该集合中被选择(13.4)。
——对于指向数据成员的指针类型的一个非类型template-parameter,限定转换(4.4)被应用。【例:
template<const int* pci> struct X { /* ... */ }; int ai[10]; X<ai> xi; // 数组转为指针以及限定转换 struct Y { /* ... */ }; template<const Y& b> struct Z { /* ... */ }; Y y; Z<y> z; // 无转换,但注意额外的cv限定 template<int (&pa)[5]> struct W { /* ... */ }; int b[5]; W<b> w; // 无转换 void f(char); void f(int); template<void (*pf)(int)> struct A { /* ... */ }; A<&f> a; // 选择f(int)
——例结束】
14.3.3 模板模板实参
1、对应于一个模板template-parameter的一个template-argument应该是一个类模板的名字,被表达为id-expression。当用相应的形参来匹配模板模板实参时,只有基本的类模板才被考虑;部分特化不被考虑,即使其形参列表与模板模板形参列表匹配。【译者注:
template <typename T> struct A { enum { VALUE = 0 }; }; template <> struct A<int> { enum { VALUE = 1 }; }; template <template <typename T> class C> void f1(void) { } template <template <int> class C> void f2(void) { } int main (int, char**) { f1<A>(); // OK f2<A>(); // error }
】
2、任何与基本类模板相关联的部分特化(14.5.4)都被考虑,当基于模板template-parameter的一个特化被实例化时。如果一个特例化在实例化点上不可见,并且只有当它被选择时才可见,那么程序是不良形式的;不需要诊断。【例:
template<class T> class A { // 基本模板 int x; }; template<class T> class A<T*> { // 部分特化 long x; }; template<template<class U> class V> class C { V<int> y; V<int*> z; }; C<A> c; // 在C<A>内的V<int>使用基本模板,因此c.y.x具有int类型 // 在C<A>内的V<int*>使用部分特化,因此,c.z.x具有long类型
】【译者注:
template <int N> struct A { int a; }; template <typename T, T V, template <int N> class C> struct S { C<V> c; }; int a; int main (int, char**) { S<int, 100, A>(); // OK S<void*, 0, A>(); // OK S<void*, &a, A>(); // error }
】