Item 47 用trait类提供类型信息

本文详细介绍了STL中的迭代器分类及其使用方法,包括输入、输出、前向、双向及随机访问迭代器,并展示了如何利用trait类实现不同迭代器类型的智能判断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

STL主要由容器、迭代器和算法这三类模板组成。也包含一些工具模板,比如:

template<typename IterT, typename DistT> void advance(IterT& it, DistT d); // it += d

在实现上,只有支持随机访问的迭代器才能用+=,功能稍弱的只能用++和--重复执行。

迭代器分5类:
1> Input Iterator 只能前行,一次一步,指谁读谁,而且只能读一次。代表:istream_iterator
2> Output Iterator 与上同。用于写操作。代表:ostream_iterator

这两类功能最弱,只能用于那些单行的算法(one-pass)。

3> Forward Iterator 前行,一步,可读写多次。所以可用于多行算法(multi-pass)。代表:slist
4> Bidirectional Iterator 前后双行,一步,可读写多次。代表:list、set、multiset、map、multimap
5> Random Access Iterator 前后双行,N步,可读写多次。代表:vector、deque、string

使用上面的迭代器来实现advance,大致是下面的样子:

template<typename IterT, typename DistT> void advance(IterT& iter, DistT d) { if (iter is a random_access_iterator) { iter += d; } else { if (d >= 0) { while (d--) ++iter; } else { while (d++) --iter; } } }

我们如何才能在编译时知道一个迭代器的类型呢?

C++的世界使用trait类来实现这个功能。下面是迭代器的trait类模板:

template<typename IterT> struct iterator_traits;

● 先为这五类迭代器定义五个标签,每个标签代表一种迭代器。注意每个标签都是空的,它们只有名字而已。

struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag: public input_iterator_tag {}; struct bidirectional_iterator_tag: public forward_iterator_tag {}; struct random_access_iterator_tag: public bidirectional_iterator_tag {};

● 用这些标签给每个容器的迭代器定义一个iterator_category类型。这样,每个迭代器就都有了个叫做 iterator_category 的内置类型。

template < ... > class deque { public: class iterator { public: typedef random_access_iterator_tag iterator_category; ... }; ... }; template < ... > class list { public: class iterator { public: typedef bidirectional_iterator_tag iterator_category; ... }; ... };

● 然后让trait类重复一遍该定义。这样迭代器的trait类就能识别所有迭代器的category信息了。

template<typename IterT> struct iterator_traits { typedef typename IterT::iterator_category iterator_category; ... };

● 为了让C++的指针也支持trait类,需要再重复一遍该定义

template<typename IterT> // partial template specialization struct iterator_traits<IterT*> // for built-in pointer types { typedef random_access_iterator_tag iterator_category; ... };

● 于是,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)) ... }

不过,如此实现的advance有两个问题:其中之一将在Item 48里讨论。先介绍另一个:
iterator_traits<IterT>::iterator_category其实在编译时就可以决定了。if语句是在运行时对其进行的判断。
我们编译时就可以解决的问题,不应该留到后面。

对于advance在编译时对类型的判断,我们可以用“重载”来实现。

template<typename IterT, typename DistT> void doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag) { iter += d; } template<typename IterT, typename DistT> void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag) { if (d >= 0) { while (d--) ++iter; } else { while (d++) --iter; } } template<typename IterT, typename DistT> void doAdvance(IterT& iter, DistT d, std::input_iterator_tag) { if (d < 0 ) { throw std::out_of_range("Negative distance"); } while (d--) ++iter; } template<typename IterT, typename DistT> void advance(IterT& iter, DistT d) { doAdvance(iter, d, typename std::iterator_traits<IterT>::iterator_category()); }

● STL中广泛使用了trait类,如char_traits和numeric_limits。TR1中的:
is_fundamental<T>
is_array<T>
is_base_of<T1, T2>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值