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>