迭代器相应类型有五种,
iterator_traits
类专门用来“萃取”迭代器的特性。
下面的代码中,I
和T
表示的是任意迭代器的类型。
泛化版本:
template <class I>
struct iterator_traits {
typedef typename I::value_type value_type;
typedef typename I::difference_type difference_type;
typedef typename I::reference reference;
typedef typename I::pointer pointer;
typedef typename I::iterator_category iterator_category;
};
型别必须加上关键词
typename
,因为I
是一个模板参数,在它被编译器具现化之前,编译器对I
一无所知,并不知道I::value_type
是型别还是成员函数还是成员数据。
针对原生指针而设计的特化版本:
template <class T>
struct iterator_traits<T*> {
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T &reference;
typedef T *pointer;
typedef random_access_iterator_tag iterator_category;
};
template <class T>
struct iterator_traits<const T*> {
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef const T& reference;
typedef const T *pointer;
typedef random_access_iterator_tag iterator_category;
};
如果你希望你开发的容器能与STL
交互,一定要为你的容器的迭代器定义这五种类型。
value_type
value_type
是指迭代器所指对象的型别。
difference_type
difference_type
表示两个迭代器之间的距离,它也可以表示一个连续容器的最大容量。如STL
的count()
:
template <class I, class T>
typename iterator_traits<I>::difference_type count(I first, I last, const T &value)
{
typename iterator_traits<I>::difference_type n = 0;
while (first != last) {
++n;
++first;
}
return n;
}
对原生指针而言,使用ptrdiff_t
作为其difference_type
类型。
reference_type
迭代器可分为两种:不允许改变所指对象的是const iterator
;允许改变所指对象的是mutable iterator
。
C++
中可以通过reference
传值。
如果某个迭代器p
的value_type
是T
,
(1)
p
是const iterator
,*p
得到的是右值,类型为const T&
;
(2)
p
是mutable iterator
,*p
得到的是左值,类型为T&
。
pointer_type
pointer
和reference
在C++
中有非常密切的关联。我们可以传一个引用,令它代表所迭代器所指之物;也可以传一个值,令它代表所指之物的地址。
iterator_category
根据移动特性和实施操作,迭代器被分为五类:
(1)
InputIterator
:只读迭代器。
(2)
OutputIterator
:只写迭代器。
(3)
ForwardIterator
:单向移动迭代器,支持++
操作。
(4)
BidirectionalIterator
:双向移动迭代器,支持++
和--
操作。
(5)
RandomAccessIterator
:随机访问迭代器,支持++
、--
、+=
、-=
、<
等操作。
以上五种迭代器的分类和从属关系,如下图所示:
直线与箭头代表的不是继承关系,而是
concept
(概念)与refinement
(强化)的关系。顺着箭头的方向,强化的程度越高。
下面定义五个类,代表五种迭代器类型:
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 {};
这些类只作为标记,所以不需要任何成员。
运用继承机制是为了传递调用:如果没有匹配函数实参的形参类型,就去匹配实参类型的基类。
任何一个迭代器,其类型落在“该迭代器所隶属之各类型中,最强化的那个”。比如int*
,它既是RandomAccessIterator
,也是BidirectionalIterator
、ForwardIterator
、InputIterator
,那么它的类型应该归属为RandomAccessIterator
。
而STL
算法的命名规则是:以算法所能接受之最低阶迭代器类型,来为迭代器型别参数命名。
总结
- 设计适当的相应型别(5种)是迭代器的责任。
- 设计适当的迭代器是容器的责任。唯有容器本身才知道怎样的迭代器能遍历自己,并执行前进、后退、取值、取用成员等操作。
- 算法和容器独立开发,以迭代器作为两者的粘合剂。