C++的typename和template
Table of Contents
1 typename 和 template 的使用
typename 和template 并不仅仅出现在模板参数中,例如下面的这个例子:
double const pi = acos(-1); template<class T> struct iterator_traits; template<class FwdIterator1, class FwdIterator2> void iter_swap(FwdIterator1 i, FwdIterator2 j) { iterator_traits<FwdIterator1>::value_type* pi = &*i; ... }
编译器必须检查iter_swap是否存在语法错误。这里,iterator_traits的::value_type如果是一个类型,那么第7行就是合法声明。但是,如果它是一个值:
template<class T> struct iterator_traits { enum {value_type = 0}; }
这种情况下,iter_swap可能还是有效的,但是第7行代码会变成将一个数值乘以pi,然后对其进行赋值:
(iterator_traits<FwdIterator1>::value_type* pi) = &*i;
这显然不是我们想要的。
如果委员会决定编译器应该推导出value_type必须是一个类型,那会怎么样呢?请看下面的例子:
class number { public: template<class U> number& operator=(U const &); int& operator*() const; }; number operator*(number, double); template <class T> struct iterator_traits { static number value_type; };
2 消除歧义
我们可以用typename告诉编译器,一个依赖性名表示的是一个依赖性的类型(dependent type):
template<class FwdIterator1, class FwdIterator2> void iter_swap(FwdIterator1, FwdIterator2) { typename iterator_traits<FwdIterator1>::value_type* pi = &*i; }
再看看下面的例子:
template<class T> int f(T& x) { return x.convert<3>(pi); }
这里T::convert可能是一个成员函数模板,但也可能是一个数据成员。如果它是数据成员,f返回(x.convert<3)>pi。
同样采用template来消除歧义:
template<class T> int f(T& x) { return x.template convert<3>(pi); }
3 typename 规则
关键字typename只能用于模板声明和定义中,包括函数模板或成员函数模板的返回类型中,类模板的成员函数定义的返回类型中,或类模板中的嵌套类的成员函数定义的返回类型中,类模板的静态成员定义的类型修饰符中,或类模板中的嵌套类的静态成员定义的类型修饰符中。
关键字typename不允许用在基类修饰符或成员初始化列表中,在这些上下文中,一个依赖于模板参数的限定性名字被假定为一个类型名。
### 必须使用typename的场合
必须标明依赖关系的地方需要使用typename。
template<class C> struct st { typename C::value_type x; };
C::value_type依赖C。
每个依赖名都要使用一个typename:
template<class T, typename ntp<T>::type value = typename ntp<T>::type()> struct init {};
4 禁止使用typename的场合
- 不能用在模板之外任何地方。
- 不允许用在非限定性的名字上(没有::前缀的)。
- 不允许用在基类的名字上。
- 显示特化不是一个模板声明,也不能使用typename。
5 template 规则
当成员模板特化的名字出现在一个后缀表达式中的.或->之后,或者出现一个限定标识中的嵌套的名字修饰符之后,并且后缀表达式或限定标识显示依赖于一个模板参数时,成员模板名字必须加template关键字作为前缀,否则改名字就被假定为一个非模板的名字。
如果后缀表达式或者限定标识不是出现在一个模板的作用域时,成员模板的名字就不应该加上template关键字作为前缀。
6 必须使用template的场合
在通过“.”,“->”,“::”限定的依赖名访问成员模板之前,template关键字必不可少。
template<class T> void f(T& x, T& y) { int n = x.template convert<int>(); int m = y->template convert<int>(); } template<class T> struct other; template<class T> struct dirived : other <T>::template base<int> {};
7 禁止使用template的场合
禁止用在模板之外的任何地方,包括显示(完全)模板特化。禁止用在using声明中。