迭代器
Concepts + iterator_tag
迭代器有 5 大类 concepts
,分别是InputIterators
、OutputIterators
、ForwardIterators
、BidirecitionalIterators
和RandomAccessIterators
。
InputIterators
这个concept必须满足的requirements有:
EqualityComparable
。Assignable
。*iter
或者iter->
,进行dereference
。++iter
同时它也有一些限制contraints
(这些是用来区分Input
和Forward
的参考):
- 只保证可读。即
x = *iter
是可行的,但是*iter = x
这种行为是未定义的。 - 不能以两个 iter 指向同一区间内的两个不同的元素,即
iter1 = iter2++
然后继续同时使用iter1
和iter2
是不合理的。
OutputInterators
其 requirements 同 InputIterators
。
同时它也有一些限制contraints
:
- 只保证可写。
- 不能以两个 iter 指向同一区间内的两个不同的元素。
ForwardIterator
其 requirements 同 InputIterators
。但消除了Input
的约束。
BidirectionalIterators
扩充了Forward
的 reuqirements:
--iter
- reverse_iterator 适配器
RandomAccessIterators
扩充了Bidirectional
的 requirements:
LessThanComparable
[]
iter + n
iter - n
iter1 - iter2
在实际编程中,我们使用 5 种 iterator_tag
来表示上述conceptes
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 traits
迭代器最大的用处就是用来遍历元素的,因此常常和元素的类型(associated type
)打交道。
问题1: 那么我们如何获得这个元素的类型呢?
一种常用的办法就是使用一个wrapper,即forward + template type deduction
template<typename T> inline void f(I iter){
f_impl(iter, *iter); // forward
}
template<typename I, typename T> void f_impl(I iter, T t){ // *iter to deduction type T
T value = ...;
}
这种方法一个最大的缺陷就是无法推导函数的返回值。
继而我们使用nested typedef
,在函数体内部typedef A value_type
,但是对于指针类型,无法通过class::value_type
获取元素类型。因此我们定义了一个模板类iterator_traits
并针对指针类型特化。
template<typename Iter>
class iterator_traits{
public:
typedef Iter::value_type value_type;
typedef Iter::difference_type difference_type;
typedef Iter::pointer pointer;
typedef Iter::reference reference;
typedef Iter::iterator_catagory iterator_category;
};
template<typename T>
class iterator_traits < T* > {
typedef std::remove_const<T>::type value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
typedef random_access_iterator_tag iterator_category;
};
问题2:如果iterator_traits
模板参数不是迭代器类型呢?
一般情况下,如果模板实参不是迭代器类型,那么我们保持iterator_traits
里的内容为空。
那么如何区分?
1、首先参数Iter
必须定义了iterator_category
这个类型名——通过函数模板重载匹配和SFINAE
原则实现。
template<typename Tp>
class __has_iterator_category{
struct __twoBytes{ // only for test
char oneByte;
char twoByte;
};
template <class Up> static __twoBytes test(...);
template <class Up> static char test(typename Up::iterator_category*);
public:
static const bool value = sizeof(test<Tp>(nullptr)) == 1;
};
2、如果定义了iterator_category
,如何确定就是迭代器——如果可以转化为两个基类tag:input_iterator_tag
和output_iterator_tag
,那么就是迭代器类型。
std::is_convertible<typename Iter::iterator_category, input_iterator_tag>::value ||
std::is_convertible<typename Iter::iterator_category, output_iterator_tag>::value
问题3:自定义的迭代器如何使其支持iterator_traits
针对不同的 iterator concept,算法会采取不同的处理方法,以达到更佳的性能,而这一点就是通过iterator_traits
这个作为中间件实现的。那么我们自定义迭代器时,必须使其支持iterator_traits
。主要有以下两种方法实现:
- 定义
iterator_traits
要求的 5 种typedef
- 继承自公共基类
iterator
template<typename Category, typename T, typename Diffence = ptrdiff_t,
typename Pointer = T*, typename Reference = T&>
struct iterator
{
typedef T value_type;
typedef Diffence difference_type;
typedef Pointer pointer;
typedef Reference reference;
typedef Category iterator_category;
};
辅助函数
iterator
库中提供了一些常用的辅助函数来操纵迭代器。prev
、next
,advance
,distance
。针对不同的迭代器类型,提供不同的实现版本。
同样的对于模板参数我们需要检查实参是不是迭代器类型。
对于advance
和distance
函数,因为要根据不同的迭代器类型进行特化处理,因此使用iterator_category
作为区分
template<typename InputIter> inline
void advance(InputIter& iter, typename iterator_traits<InputIter>::difference_type n)
{
__advance(iter, n, typename iterator_traits<InputIter>::iterator_category());
}
对于next
,需要增加一个模板参数,检查是否为input_iterator
,对prev
则要检查是否为bidirectional_iterator
template<typename Iter, typename InputIter, bool = __has_iterator_category<Iter>::value>
struct __has_iterator_category_and_convertible_to
: public std::integral_constant<bool, is_convertible<typename Iter::iterator_category, InputIter>::value>
{};
template<typename Iter, typename InputIter, false>
struct __has_iterator_category_and_convertible_to
: public std::false_type {};
template<typename Iter>
struct __is_input_iterator
: public __has_iterator_category_and_convertible_to < Iter, input_iterator_tag > {};
iterator adaptor
C++11后提供了 4 类 迭代器适配器:insert
、reverse
、stream
和move
迭代器。
reverse_iterator
根据原来的迭代器创建一个反向的迭代器。反向规则是:保持指向的associate element
位置不变,但是在deference
时,取前一个位置的值。所以rbegin
指向的end
,但*rbegin
是一个有效值,而*rend
不是一个有效值。
insert iterator
是一种output iterator
,因此只有写动作有意义iter = value
,其写动作会被转化为调用对应容器的insert
方法。因此每一种inserter只对提供了对应insert
方法的容器适用。
back_insert_iterator(Cont) = value
==> Cont.push_back(value)
front_insert_iterator(Cont) = value
==> Cont.push_front(value)
insert_iterator(Cont, pos) = value
==> Cont.insert(pos, value)
stream iterator
将stream作为操作的对象。分为istream
和ostream
。
ostream
是一个output iterator
,其操作和insert
一样,只不过是对output stream
写入元素了。
istream
是一个input iterator
,因此是只读的,用来从input stream
读取元素,同时为了表示读取结束,我们用无参数的istream_iterator
来构造end-of-stream
迭代器。