条款42:了解typename的双重意义
2016-05-12 20:07
274 查看
Item 42: Understand the two meanings of typename.
时至今日还有人在论坛里问模板参数前的
答案是没有区别!有人觉得
然而
下面的代码会编译错:
发生编译错误是因为编译器不知道
直到确定了
然而当模板被解析时,
事实上类型
模板中依赖于模板参数的名称称为从属名称(dependent name), 当一个从属名称嵌套在一个类里面时,称为嵌套从属名称(nested
dependent name)。 其实
dependent type name)。
嵌套从属名称是需要用
如果你把
错误输出:
模板中的嵌套从属名称是需要
例如
C++提供了一系列的traits模板,用来提供类型信息。比如:
其实上述模板方法也可以不同traits来实现,比如:
但traits提供了更加一致的使用方式以及容器实现的灵活性,模板代码也简洁了不少。 尽管如此,程序员还是懒惰的。我们倾向于用
虽然
时至今日还有人在论坛里问模板参数前的
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<T>继承自
Base<T>::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么?
相关文章推荐
- PAT (Basic Level) Practise (中文)1042. 字符统计(20)
- android animation——动画的基本属性
- HDU 5366 dp 递推
- 使用AES加密用户密码
- convert: not found
- hive入门
- 多线程之:模拟实现线程池的工作原理
- LintCode:重哈希
- cordova+ios插件
- 运算符重载(摘抄)
- cordova+ios插件
- poj 1151 Atlantis(线段树+离散化+扫描线)
- 【大数据-第二期】java基础第六天作业
- 关于Android事件派发流程的理解
- 解决Android软键盘和表情面板切换界面闪动问题
- 一个让我很无语的bug
- 显示gcc内置宏
- PAT 1022
- Eviews 9.0新功能——估计方法(ARDL、面板自回归、门限回归)
- 第七周项目3——用多文件组织多个类的程序