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声明中。


By .