分享
 
 
 

[翻译] Effective C++, 3rd Edition, Item 47: 为类型信息使用 traits classes(特征类)(下)

王朝c/c++·作者佚名  2006-02-17
窄屏简体版  字體: |||超大  

(点击此处,接上篇)

iterator_traits 通过两部分实现这一点。首先,它强制要求任何 user-defined iterator(用户定义迭代器)类型必须包含一个名为 iterator_category 的嵌套 typedef 用以识别适合的 tag struct(标签结构体)。例如,deque 的 iterators(迭代器)是随机访问的,所以一个 deque iterators 的 class 看起来就像这样:

template < ... > // template params elided

class deque {

public:

class iterator {

public:

typedef random_access_iterator_tag iterator_category;

...

};

...

};

然而,list 的 iterators(迭代器)是双向的,所以它们是这样做的:

template < ... >

class list {

public:

class iterator {

public:

typedef bidirectional_iterator_tag iterator_category;

...

};

...

};

iterator_traits 仅仅是简单地模仿了 iterator class 的嵌套 typedef:

// the iterator_category for type IterT is whatever IterT says it is;

// see Item 42 for info on the use of "typedef typename"

template<typename IterT>

struct iterator_traits {

typedef typename IterT::iterator_category iterator_category;

...

};

这样对于 user-defined types(用户定义类型)能很好地运转。但是对于本身是 pointers(指针)的 iterators(迭代器)根本不起作用,因为不存在类似于带有一个嵌套 typedef 的指针的东西。iterator_traits 实现的第二个部分处理本身是 pointers(指针)的 iterators(迭代器)。

为了支持这样的 iterators(迭代器),iterator_traits 为 pointer types(指针类型)提供了一个 partial template specialization(部分模板特化)。pointers 的行为类似 random access iterators(随机访问迭代器),所以这就是 iterator_traits 为它们指定的种类:

template<typename IterT> // partial template specialization

struct iterator_traits<IterT*> // for built-in pointer types

{

typedef random_access_iterator_tag iterator_category;

...

};

到此为止,你了解了如何设计和实现一个 traits class:

识别你想让它可用的关于类型的一些信息(例如,对于 iterators(迭代器)来说,就是它们的 iterator category(迭代器种类))。

选择一个名字标识这个信息(例如,iterator_category)。

提供一个 template(模板)和一系列 specializations(特化)(例如,iterator_traits),它们包含你要支持的类型的信息。

给出了 iterator_traits ——实际上是 std::iterator_traits,因为它是 C++ 标准库的一部分——我们就可以改善我们的 advance 伪代码:

template<typename IterT, typename DistT>

void advance(IterT& iter, DistT d)

{

if (typeid(typename std::iterator_traits<IterT>::iterator_category) ==

typeid(std::random_access_iterator_tag))

...

}

这个虽然看起来有点希望,但它不是我们想要的。在某种状态下,它会导致编译问题,但是我们到 Item 48 再来研究它,现在,有一个更基础的问题要讨论。IterT 的类型在编译期间是已知的,所以 iterator_traits<IterT>::iterator_category 可以在编译期间被确定。但是 if 语句还是要到运行时才能被求值。为什么要到运行时才做我们在编译期间就能做的事情呢?它浪费了时间(严格意义上的),而且使我们的执行码膨胀。

我们真正想要的是一个针对在编译期间被鉴别的类型的 conditional construct(条件结构)(也就是说,一个 if...else 语句)。碰巧的是,C++ 已经有了一个得到这种行为的方法。它被称为 overloading(重载)。

当你重载某个函数 f 时,你为不同的 overloads(重载)指定不同的 parameter types(形参类型)。当你调用 f 时,编译器会根据被传递的 arguments(实参)挑出最佳的 overload(重载)。基本上,编译器会说:“如果这个 overload(重载)与被传递的东西是最佳匹配的话,就调用这个 f;如果另一个 overload(重载)是最佳匹配,就调用它;如果第三个 overload(重载)是最佳的,就调用它”等等。看到了吗?一个针对类型的 compile-time conditional construct(编译时条件结构)。为了让 advance 拥有我们想要的行为方式,我们必须要做的全部就是创建一个包含 advance 的“内容”的重载函数的多个版本(此处原文有误,根据作者网站勘误修改——译者注),声明它们取得不同 iterator_category object 的类型。我为这些函数使用名字 doAdvance:

template<typename IterT, typename DistT> // use this impl for

void doAdvance(IterT& iter, DistT d, // random access

std::random_access_iterator_tag) // iterators

{

iter += d;

}

template<typename IterT, typename DistT> // use this impl for

void doAdvance(IterT& iter, DistT d, // bidirectional

std::bidirectional_iterator_tag) // iterators

{

if (d >= 0) { while (d--) ++iter; }

else { while (d++) --iter; }

}

template<typename IterT, typename DistT> // use this impl for

void doAdvance(IterT& iter, DistT d, // input iterators

std::input_iterator_tag)

{

if (d < 0 ) {

throw std::out_of_range("Negative distance"); // see below

}

while (d--) ++iter;

}

因为 forward_iterator_tag 从 input_iterator_tag 继承而来,针对 input_iterator_tag 的 doAdvance 版本也将处理 forward iterators(前向迭代器)。这就是在不同的 iterator_tag structs 之间继承的动机。(实际上,这是所有 public inheritance(公有继承)的动机的一部分:使针对 base class types(基类类型)写的代码也能对 derived class types(派生类类型)起作用。)

advance 的规范对于 random access(随机访问)和 bidirectional iterators(双向迭代器)允许正的和负的移动距离,但是如果你试图移动一个 forward(前向)或 input iterator(输入迭代器)一个负的距离,则行为是未定义的。在我检查过的实现中简单地假设 d 是非负的,因而如果一个负的距离被传入,则进入一个直到计数降为零的非常长的循环。在上面的代码中,我展示了改为一个异常被抛出。这两种实现都是正确的。未定义行为的诅咒是:你无法预知会发生什么。

给出针对 doAdvance 的各种重载,advance 需要做的全部就是调用它们,传递一个适当的 iterator category(迭代器种类)类型的额外 object 以便编译器利用 overloading resolution(重载解析)来调用正确的实现:

template<typename IterT, typename DistT>

void advance(IterT& iter, DistT d)

{

doAdvance( // call the version

iter, d, // of doAdvance

typename // that is

std::iterator_traits<IterT>::iterator_category() // appropriate for

); // iter's iterator

} // category

我们现在能够概述如何使用一个 traits class 了:

创建一套重载的 "worker" functions(函数)或者 function templates(函数模板)(例如,doAdvance),它们在一个 traits parameter(形参)上不同。与传递的 traits 信息一致地实现每一个函数。

创建一个 "master" function(函数)或者 function templates(函数模板)(例如,advance)调用这些 workers,传递通过一个 traits class 提供的信息。

traits 广泛地用于标准库中。有 iterator_traits,当然,再加上 iterator_category,提供了关于 iterators(迭代器)的四块其它信息(其中最常用的是 value_type —— Item 42 展示了使用它的示例)。还有 char_traits 持有关于 character types(字符类型)的信息,还有 numeric_limits 提供关于 numeric types(数值类型)的信息,例如,可表示值的最小值和最大值,等等。(名字 numeric_limits 令人有些奇怪,因为关于 traits classes 更常用的惯例是以 "traits" 结束,但是它就是被叫做 numeric_limits,所以 numeric_limits 就是我们用的名字。)

TR1(参见 Item 54)引入了一大批新的 traits classes 提供关于类型的信息,包括 is_fundamental<T>(T 是否是一个 built-in type(内建类型)),is_array<T>(T 是否是一个 array type(数组类型)),以及 is_base_of<T1, T2>(T1 是否和 T2 相同或者是 T2 的一个 base class(基类))。合计起来,TR1 在标准 C++ 中加入了超过 50 个 traits classes。

Things to Remember

traits classes 使关于类型的信息在编译期间可用。它们使用 templates(模板)和 template specializations(模板特化)实现。

结合 overloading(重载),traits classes 使得执行编译期类型 if...else 检验成为可能。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有