STL源码分析——迭代器(一)

2 篇文章 0 订阅
2 篇文章 0 订阅

迭代器


Concepts + iterator_tag

迭代器有 5 大类 concepts,分别是InputIteratorsOutputIteratorsForwardIteratorsBidirecitionalIteratorsRandomAccessIterators

InputIterators

这个concept必须满足的requirements有:

  • EqualityComparable
  • Assignable
  • *iter或者iter->,进行dereference
  • ++iter

同时它也有一些限制contraints(这些是用来区分InputForward的参考):

  • 只保证可读。即x = *iter是可行的,但是*iter = x这种行为是未定义的。
  • 不能以两个 iter 指向同一区间内的两个不同的元素,即iter1 = iter2++然后继续同时使用iter1iter2是不合理的。

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_tagoutput_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。主要有以下两种方法实现:

  1. 定义iterator_traits要求的 5 种 typedef
  2. 继承自公共基类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库中提供了一些常用的辅助函数来操纵迭代器。prevnextadvancedistance。针对不同的迭代器类型,提供不同的实现版本。
同样的对于模板参数我们需要检查实参是不是迭代器类型。
对于advancedistance函数,因为要根据不同的迭代器类型进行特化处理,因此使用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 类 迭代器适配器:insertreversestreammove迭代器。

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作为操作的对象。分为istreamostream
ostream是一个output iterator,其操作和insert一样,只不过是对output stream写入元素了。
istream是一个input iterator,因此是只读的,用来从input stream读取元素,同时为了表示读取结束,我们用无参数的istream_iterator来构造end-of-stream迭代器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值