《Effective C++》学习笔记——条款42

本文探讨了C++中typename关键字的双重定义及其在模板编程中的应用,尤其是在处理嵌套从属名称时的重要性。




七、模板与泛型编程


条款42、了解 typename 的双重定义



当我们声明template类型参数,class 和 typename 的意义完全相同,比如:

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

但是,C++并不总是把他俩看做等价的。


例1

假设,我们有个 template function,接受一个STL兼容容器为参数,容器内持有的对象可被赋值为int。

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

iter的类型是 C::const_iterator,实际的类型取决于参数C。
template内出现的名称如果相依于某个template参数,称之为 dependent name(从属名称),如果从属名称在 class 内呈嵌套状,我们称它为嵌套从属名称,就像 iter一样。
而另一个local变量 value,是int类型,并不依赖任何template参数,这叫 non-dependent name(非从属名称)。

嵌套从属名称 可能会导致 解析困难。

template<typename C>
void print2nd(const C& container)
{
    C::const_iterator* x;
    ...
}

乍一看,变量x,是我们声明的一个指向 C::const_iterator的指针变量。
因为,我们已经知道了 C::const_iterator是一个类型,但是,如果它不是一个类型呢?如果 C有个static成员变量而碰巧被命名为 const_iterator,或如果 x 碰巧是一个global变量名称,那么里面的 * 可能就不是指针相关,而是 乘号。
虽然,听起来荒谬,但实际上是有可能的。

当然,C++有规则可以解析这个歧义状态:如果解析器在template中遭遇一个嵌套从属名称,它便假设这名称不是个类型,除非你告诉它是。所以,缺省情况下嵌套从属名称不是类型。
就是在 你想在template中指涉一个嵌套从属类型名称,就必须在紧邻它的前一个位置加上关键字 typename。
typename 只被用来验明嵌套从属类型名称,其他名称不该有它存在。


例2

typename 必须作为嵌套从属类型名称的前缀词
这一规则 有一个例外:
typename 不可以出现在 base_class list 内的嵌套从属名称类型名称之前,也不可以 member initialization list(成员初值列)中作为base class修饰符。

template<typename T>
class Derived: public Base<T>::Neted  {  // base class list中 不允许 typename
public:
    explicit Derived(int x): Base<T>::Nested(x)  // mem.init.list 中 不允许 typename
    {
        typename Base<T>::Nested temp;  // 嵌套从属名称既不在 base class list,也不在 mem.init.list中
        ...
    }
    ...
};


例3

写一个 function template,让它接受一个迭代器,我们将为这个迭代器所指涉的对象做一份 local副本 temp。

template<typename IterT>
void workWithIterator(IterT iter)
{
    typename std::iterator_trains<IterT>::value_type temp(*iter);
    ...
}

这里,因为 std::iterator_trains::value_type 是嵌套从属名称,所以前面加了关键字 typename。
这个东西 —— std::iterator_trains::value_type,有些长,在实际编程中,可能很多人必须换打这么长的东西,所以用到了 typedef:

template<typename IterT>
void workWithIterator<IterT iter)
{
    typedef typename std::iterator_trains<IterT>::value_type value_type;
    value_type temp(*iter);
    ...
}

typename相关规则在不同编译器上有不同的实践,所以在移植上可能会有些许微妙的问题存在。




请记住

  • 声明template参数时,前缀关键字 class 和 typename 可互换
  • 请使用关键字 typename 标识嵌套从属类型名称;但不得在 base class list 或 member initialization list 内以它作为base class 修饰符




评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值