Item 42:typename的两种用法

Item 42: Understand the two meanings of typename.

时至今日还有人在论坛里问模板参数前的typename和class有何区别:

template<typename T> class Widget;
template<class T> class Widget;

答案是没有区别!有人觉得class写起来方便就用class,有人觉得typename语义更正确就用typename。 然而typename和class对编译器而言却是不同的东西,这是本节的重点所在。

typename可以用来帮编译器识别嵌套从属类型名称,基类列表和成员初始化列表除外。

声明一个类型


typename的第一个作用在于声明一个类型。为什么类型还需要声明呢?因为编译器并不是总会知道哪个名称是个类型。 下面的代码会编译错:

template<typename C>
void print2nd(const C& container){
    if(container.size() >= 2){
        C::const_iterator it(container.begin());
        ++it;
        int value = *it;  
        cout<<value;
    }
}

发生编译错误是因为编译器不知道C::const_iterator是个类型。万一它是个变量呢? C::const_iterator的解析有着逻辑上的矛盾: 直到确定了C是什么东西,编译器才会知道C::const_iterator是不是一个类型; 然而当模板被解析时,C还是不确定的。这时我们声明它为一个类型才能通过编译:

typename C::const_iterator it(container.begin());

嵌套从属名称


事实上类型C::const_iterator依赖于模板参数C, 模板中依赖于模板参数的名称称为从属名称(dependent name), 当一个从属名称嵌套在一个类里面时,称为嵌套从属名称(nested dependent name)。 其实C::const_iterator还是一个嵌套从属类型名称(nested dependent type name)。

嵌套从属名称是需要用typename声明的,其他的名称是不可以用typename声明的。比如下面是一个合法的声明:

template<typename C>
void f(const C& container, typename C::iterator iter);

如果你把const C&也声明了typename也是要编译错的哦:

template<typename C>
void f(typename const C& container, typename C::iterator iter);

错误输出:

error: expected a qualified name after 'typename'

一个例外


模板中的嵌套从属名称是需要typename声明的,然而有一个例外情况: 在派生子类的基类列表中,以及构造函数的基类初始化列表中,不允许typename声明。 例如Derived继承自Base::Nested:

template<typename T>
class Derived: public Base<T>::Nested{  // 继承基类列表中不允许声明`typename`
public:
    explicit Derived(int x): Base<T>::Nested(x){    // 基类初始化列表不允许声明`typename`
        typename Base<T>::Nested tmp;   // 这里是要声明的
    }
};

traits


C++提供了一系列的traits模板,用来提供类型信息。比如:

template<typename IterT>
void workWithIterator(IterT it){
    typename std::iterator_traits<IterT>::value_type tmp(*it);
}

其实上述模板方法也可以不同traits来实现,比如:

template<typename container>
void workWithIterator(typename container::iterator it){
    typename container::value_type tmp(*it);
}

但traits提供了更加一致的使用方式以及容器实现的灵活性,模板代码也简洁了不少。 尽管如此,程序员还是懒惰的。我们倾向于用typedef来给这些嵌套从属名称起一些别名:

template<typename IterT>
void workWithIterator(IterT it){
    typedef typename std::iterator_traits<Iter>::value_type value_type;
    value_type tmp(*it);
}
虽然typedef typename看起来也很怪异,但你想敲很多遍typename std::iterator_traits<Iter>::value_type么?

转载地址:http://harttle.land/2015/09/09/effective-cpp-42.html
感谢作者 Harttle

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值