您的位置:首页 > 编程语言 > C语言/C++

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

2016-03-17 23:30 525 查看

七、模板与泛型编程

条款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 修饰符
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  学习笔记