C++标准ISO-IEC-14882-2003:第14章:模板-第3节:模板实参

 

14.3 模板实参

1.        对应模板形参的三种形式,模板实参也有三种形式:类型、非类型和模板。模板 id 中指定的每个模板实参的类型和形式应该与其声明时模板形参列表中相应的形参的类型和形式相匹配。【例:

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.        在模板实参中,不论对应的形参是什么形式,类型 id 与表达式之间的歧义都会被解析为类型 id 。【例:

template<class T> void f();

template<int I> void f();

void g()

{

f<int()>(); // int() is a type-id: call the first f()

}

3.        模板实参的名字在用作实参的时候必须是可访问的。【注:如果模板实参的名字在用作实参的时候是可访问的,当模板实参名字被使用的时候会产生实例化,这时就没有更进一步的访问限制了。】【例:

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; // 错误 : S 不可访问

】模板实参的类型如果为类,模板定义对于无法访问的模板实参类型的成员没有特殊的访问权限。

4.        当使用默认模板实参的时候,模板实参列表可以为空。在这种情况下,空的 <> 尖括号仍然应当以模板实参列表的形式出现。【例:

template<class T = char> class String;

String<>* p; // OK: String<char>

String* q; // 语法错误

5.        类模板特化类型的对象的显式析构函数调用可以显式指定模板实参。【例:

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.        如果模板实参的使用导致了模板特化的实例化过程非法,那么程序就是非法的。

7.        模板 id 中的模板是个重载函数模板时,重载集合中的非模板函数、模板形实参数不匹配的函数模板都会被排除掉。如果函数模板中没有匹配的模板形参,程序就是非法的。

14.3.1 模板的类型实参

1.          模板参数的实参如果为类型参数,那么它必须是个类型 id

2.          局部类型、没有连接性的类型、无名类型,和由这些类型构成的类型,都不能用作模板参数的实参。【例:

template <class T> class X { /* ... */ };

void f()

{

struct S { /* ... */ };

X<S> x3; // 错误 : 局部类型用作模板实参

X<S*> x4; // 错误 : 局部类型的指针用作模板实参

}

】【注:模板实参可以是不完整类型。】

3.          如果一个声明根据模板形参的类型获得了一个函数类型,而导致某个没有使用函数声明符语法的声明却成为了函数类型,该程序就是非法的。【例:

template<class T> struct A {

static T t;

};

typedef int function();

A<function> a; // 非法 : 这样会导致 A<function>::t 成为一个静态函数(但它声明时却不是函数语法)

14.3.1 模板的非类型实参

1.          非类型、非模板的模板实参应该有如下形式之一:

a)          整形常量表达式或枚举类型

b)         非类型模板形参的名字

c)          有外部连接性的对象或函数的地址,包括函数模板和模板 id ,但是不包括非静态成员。该地址用 & 后跟 id 表达式的形式表示;如果名字是个函数、数组,或对应的模板形参是引用,则 & 可以省略

d)         5.3.1 节中描述的成员指针。

2.          【注:字符串字面量不满足上述任何一条的需求,所以不能用作模板实参。【例:

template<class T, char* p> class X {

// ...

X();

X(const char* q) { /* ... */ }

};

X<int,"Studebaker"> x1; // 错误 : 字符串字面量用作模板实参了

char p[] = "Vivisectionist";

X<int,p> x2; // OK

】】

3.          【注:数组元素的地址,非静态成员的名字或地址,都不能用作模板实参。【例:

template<int* p> class X { };

int a[10];

struct S { int m; static int s; } s;

X<&a[2]> x3; // 错误 : 数组元素的地址

X<&s.m> x4; // 错误 : 非静态成员的地址

X<&s.s> x5; // 错误 : 应该使用 &S::s 的形式

X<&S::s> x6; // OK: 静态成员的地址

】】

4.          【注:当对应的模板形参是引用类型时,临时对象、无名字的左值、有名字但是没有外部链接的左值都不能用作模板实参。【例:

template<const int& CRI> struct B { /* ... */ };

B<1> b2; // 错误 : 模板实参会创建一个临时对象

int c = 1;

B<c> b1; // OK

】】

5.          下列的类型转换会应用于每个用作非类型模板实参的表达式上。如果非类型模板实参不能被转换为相应模板形参所要求的类型,程序就是非法的。

a)          对于整形、枚举型的非类型实参,使用整型提升和整型转换

b)         对于对象指针的非类型实参,使用修饰转换、数组 - 指针转换。【注:特别之处,空指针转换、派生类到基类的转换不会被使用。尽管 0 作为整型的非模板参数是个有效的实参,但它能用作指针类型的非模板实参。】

c)          对于对象引用的非类型模板形参,不会使用类型转换。引用所指向的类型,可以比模板实参的 cv 修饰程度更深、或相同。模板形参直接绑定于模板实参上,模板实参必须是个左值。

d)         对于函数指针的非类型形参,只使用数组 - 指针类型转换。如果模板实参代表一个重载函数集合(或其指针),合适的函数会从该集合中选取。

e)          对于函数引用的非类型形参,不会使用类型转换。如果模板实参代表一个重载函数集合,合适的函数会从该集合中选取。

f)          对于成员函数指针的非类型形参,不会使用类型转换。如果模板实参代表一个重载函数集合,合适的函数会从该集合中选取。

g)          对于数据成员指针的非类型形参,使用修饰转换。【例:

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.1 模板的模板实参

1.          类型为模板的模板形参对应的模板实参必须是类模板的名字,使用 id 表达式的形式表示。在匹配模板的模板实参时,只有对应模板形参的原始类模板被考虑候选;该类模板的偏特化不在考虑之列,即使其参数列表能够匹配外围模板的模板形参。

2.          当基于模板的模板形参的特化进行实例化的时候,关联于原始类模板的偏特化才被考虑候选。如果实例化的时候,该特化不可见,并且它被当做已经可见了一样选用,那么该程序就是非法的;不需要给出诊断信息。【例:

template<class T> class A { // 原始模板

int x;

};

template<class T> class A<T*> { // 偏特化

long x;

};

template<template<class U> class V> class C { // 类模板 C 的模板参数为类模板 V

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

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值