类别:迭代器 | 组件类型:概述 |
注意,迭代器标记函数distance_type, value_type和iterator_category是访问与迭代器关联的类型信息的较旧方法:它们是在原始 STL 中定义的。然而,草案 C++ 标准定义了一个不同且更方便的机制iterator_traits。出于向后兼容性的原因,支持这两种机制[2],但最终将移除较旧的机制。
迭代器的类别是其所建模的最具体概念:输入迭代器、输出迭代器、前进迭代器、双向迭代器或随机访问迭代器。此信息在 C++ 类型系统中通过定义五个类别标记类型来表示,input_iterator_tag, output_iterator_tag, forward_iterator_tag, bidirectional_iterator_tag和random_access_iterator_tag其中每个都对应于这些概念之一。[3]
函数iterator_category接收一个参数,一个迭代器,并返回对应于该迭代器类别的标记。也就是说,它返回一个random_access_iterator_tag如果其参数是一个指针,一个bidirectional_iterator_tag如果其参数是一个list::iterator等等。Iterator_traits以略微不同的方式提供相同的信息:如果I是一个迭代器,则iterator_traits<I>::iterator_category是一个嵌套typedef: 它属于五个类别标记类型之一。
迭代器的值类型是当迭代器解除引用时返回的对象类型。(参见输入迭代器要求中的讨论。)理想情况下,可能想要value_type只接受一个自变量,迭代器,并返回迭代器的值类型。不幸的是,这是不可能的:一个函数必须返回一个对象,类型并非对象。相反,value_type返回该值(T*) 0其中T是自变量的值类型。iterator_traits然而,类没有这个限制iterator_traits<I>::value_type是一种类型,而不是值。它是一种嵌套的typedef类型,并可在变量声明中用作函数的自变量类型或返回值类型,并可用于 C++ 类型可用于的任何其他方式。
(请注意,对于 输出迭代器,value_type函数不需要定义,因为 输出迭代器 不必拥有值类型。同样,iterator_traits<I>::value_type通常定义为voidI当
是一个输出迭代器时)distance_type迭代器的距离类型或差异类型(这些术语同义)是用于表示两个迭代器之间距离的类型。(请参阅 输入迭代器 要求中的讨论。)该函数value_type返回此信息的形式与一致:它的自变量是一个迭代器,它返回该值其中(Distance*) 0Distance是迭代器的距离类型。同样,iterator_traits<I>::difference_typeI是
的距离类型。value_type恰如distance_type,该函数I对于 输出迭代器,不需要定义,如果是迭代器的距离类型。同样,是 输出迭代器,通常定义为可以定义为
。 输出迭代器 不需要拥有距离类型。iterator_category, value_type和distance_type必须为每种类型的迭代器提供函数value_type。(不过正如上文所述,distance_type和不必为 输出迭代器 提供。)从根本上说,这仅仅是重载问题:任何定义新迭代器类型的人必须为此定义那三个函数。实际上,还有一个更方便的方法。STL 定义了五个基类,, output_iterator, input_iterator, forward_iterator和bidirectional_iteratorrandom_access_iteratoriterator_category, value_type和distance_type。函数
为那些基类定义。因此,效果是,如果您正在定义新类型的迭代器,您可以简单地从那些基类之一派生它,则迭代器标记函数将自动得到正确定义。这些基类不包含任何成员函数或成员变量,因此从其中的一个派生不应产生任何开销。(同样,请注意,基类仅为定义迭代器的人们的方便提供。如果您定义了一个类Iterforward_iterator它是一种新型 双向迭代器,您不必从基类iterator_category, value_type和distance_type已为类型(同样,请注意,基类仅为定义迭代器的人们的方便提供。如果您定义了一个类的参数正确定义,并从(同样,请注意,基类仅为定义迭代器的人们的方便提供。如果您定义了一个类派生forward_iterator通常是做到这一点的最便利的方式。)
template <class ForwardIterator1, class ForwardIterator2, class ValueType> inline void __iter_swap(ForwardIterator1 a, ForwardIterator2 b, ValueType*) { ValueType tmp = *a; *a = *b; *b = tmp; } template <class ForwardIterator1, class ForwardIterator2> inline void iter_swap(ForwardIterator1 a, ForwardIterator2 b) { __iter_swap(a, b, value_type(a)); }
本示例使用iterator_traits执行完全相同的事情。注意它简单多少:不再需要辅助函数。
template <class ForwardIterator1, class ForwardIterator2> inline void iter_swap(ForwardIterator1 a, ForwardIterator2 b) { iterator_traits<ForwardIterator1>::value_type tmp = *a; *a = *b; *b = tmp; }
本示例使用iterator_category迭代器标记函数reverse可针对双向迭代器或随机访问迭代器实施,但随机访问迭代器的算法更有效率。因此,reverse被编写为分派给迭代器类别。该分派在编译时进行,且不应产生任何运行时开销。
template <class BidirectionalIterator> void __reverse(BidirectionalIterator first, BidirectionalIterator last, bidirectional_iterator_tag) { while (true) if (first == last || first == --last) return; else iter_swap(first++, last); } template <class RandomAccessIterator> void __reverse(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag) { while (first < last) iter_swap(first++, --last); } template <class BidirectionalIterator> inline void reverse(BidirectionalIterator first, BidirectionalIterator last) { __reverse(first, last, iterator_category(first)); }
在这种情况下,iterator_traits在任何实质性方面都不会不同:仍需要使用辅助函数来分派给迭代器类别。唯一的区别是将顶级函数更改为
template <class BidirectionalIterator> inline void reverse(BidirectionalIterator first, BidirectionalIterator last) { __reverse(first, last, iterator_traits<first>::iterator_category()); }
[1] 输出迭代器既没有距离类型也没有值类型;事实上,在很多方面,输出迭代器并不是真正的迭代器。输出迭代器没有值类型,因为无法从输出迭代器获取值,只能通过它写入值。同样,它们没有距离类型,这是因为无法找出从一个输出迭代器到另一个输出迭代器的距离。求距离需要比较相等,而输出迭代器不支持operator==.
[2] 该iterator_traits类依赖于一种称为部分专门化的 C++ 特性。当今许多编译器都没有实现完整标准;特别是,许多编译器不支持部分专门化。如果您的编译器不支持部分专门化,那么您将无法使用iterator_traits,并且您将不得不继续使用较早的迭代器标记函数。
[3] 注意,该列表中不包含平凡迭代器。平凡迭代器概念仅出于概念清晰的目的而引入;STL 实际上没有定义任何平凡迭代器类型,因此不需要平凡迭代器标记。事实上,有一个强有力的理由不定义它:C++ 类型系统没有提供任何方法来区分作为一个平凡迭代器(即一个不属于数组一部分的对象的指针)使用的指针和作为一个随机访问迭代器使用的指针。对于数组来说,