STL之迭代器及traits编程

来源:互联网 发布:如何在淘宝买东西便宜 编辑:程序博客网 时间:2024/05/02 04:58
    不论是泛型思维或STL的实际运用,迭代器都扮演着重要的角色。STL的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶着剂将他们撮合在一起,这就是iterator!iterator是一种行为类似指针的对象,而指针的各种行为中最常见也最重要的便是内容提领(dereference) 和成员访问(member access),因此,迭代器最重要的编程工作就是对 operator* 和 operator-> 进行重载工作。

    迭代器相应型别获取的解决方法:利用function template的参数推导(argument deducation)机制,编译器会自动进行模板参数的推导。
template <class I, class T>void func_impl(I iter, T t){    T tmp; // 这里T就是迭代器所指之物的型别    // ... 这里做原本func 应该做的全部工作} template <class I>inlinevoid func(I iter){    func_impl(iter, *iter); //func的工作全部移往func_impl} int main(){    int i;    func(&i);}
    Partial Specialization(偏特化):如果class template拥有一个以上的template 参数,我们可以针对其中某个(数个,但非全部)template 参数进行特化工作。偏特化是提供另一份template定义式,而其本身仍为templatized 。例子,面对一下这么一个类模板:
template <typename T>class C {...};
我们很容易接受它有一个形式的如下的partial specialization:
template <typename T>class C<T*> {...};
这个特化版本仅适用于“T为原生指针”的情况。有了这项利器,我们可以针对“迭代器模板参数为指针者”,设计特化版本的迭代器。
template <class I>struct iterator_traits{ // traits意为“特性”    typedef typename I::value_type value_type; // I内部定义有自己的value type};
通过这个traits的作用,萃取出来的value_type就是I::value_type。则我们就可以这样定义函数返回型别:
template <class I>typename iterator_traits<I>::value_typefunc(I ite){ return *ite; }};
    下面是一个迭代器为原生指针的偏特化版本:
template <class T>typename iterator_traits<T*>{    typedef T value_type;};
    但是,针对“指向常数对象的指针”, iterator_traits<const int*>::value_type 获得的是const int,我们用它声明一个无法赋值的暂时变量也没什么用。因此,如果一个迭代器是一个pointer_to_const,我们应该设法令其value type为一个 non-const型别:
template <class T>struct iterator_traits<const T*>{    typedef T value_type;};
    根据经验,最常用到的迭代器相应型别有五种:value type, difference type, pointer, reference, iterator catagoly。
template <class I>struct iterator_traits{    typedef typename I::value_type value_type;    typedef typename I::iterator_category  iterator_category;    typedef typename I::difference_type  difference_type;    typedef typename I::pointer pointer;    typedef typename I::reference reference;};
iterator_traits必须针对传入之型别 pointer 及 pointer-to-const 者,设计特化版本。

    迭代器相应型别之一:value type
所谓value type是指迭代器所指对象的型别。任何一个打算与STL算法有完美搭配的class,都应该定义自己的value type内嵌型别。该型别做法如上所述。

    迭代器相应型别之二:difference type
difference type用来表示两个迭代器之间的距离,因此它可以用来表示一个容器的最大容量。如果一个泛型算法提供计数功能,其传回值就必须使用迭代器的 difference type:
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;    for( ; first != last; ++first)        if(*first == value) ++n;    return n;}
    针对相应型别difference type,traits的如下两个特化版本,以C++内建的ptrdiff_t作为原生指针的difference type:
template <class I>struct iterator_traits{    typedef typename I::difference_type difference_type;};//原生指针的偏特化版本template <class T>struct iterator_traits<T*>{    typedef ptrdiff_t difference_type;};//原生指针的 point-to-const 偏特化版本 template <class T>struct iterator_traits<const T*>{    typedef ptrdiff_t difference_type;};
现在,任何时候当我们需要任何迭代器I的difference type,可以这么写:
typename iterator_traits<I>::diffenence_type

    迭代器相应型别之三:reference type
从迭代器所指之物的内容是否允许改变的角度观之,迭代器分为两种:不允许改变“所指对象之内容”者,称为constant iterators;允许改变“所指对象之内容”者,称为mutable iterators。当我们对一个mutable iteraors进行提领操作时,获得的不应该是一个右值,而应该是一个左值。该型别和下一个pointer类似,具体实现见pointer实现。

    迭代器相应型别之四:pointer type
template <class I>struct iterator_traits{    typedef typename I::pointer pointer;    typedef typename I::reference reference;};//原生指针的偏特化版本template <class T>struct iterator_traits<T*>{    typedef T* pointer;    typedef T& reference;};//原生指针的 point-to-const 偏特化版本 template <class T>struct iterator_traits<const T*>{    typedef const T* pointer;    typedef const T& reference;};
    迭代器相应型别之五:iterator_category
根据移动特性及施行操作,迭代器被分为五类:
  • Input Iterator:这种迭代器所指的对象,不允许外界改变。只读!
  • Output Iterator:只写!
  • Forward Iterator:允许“写入型”算法在此种迭代器所形成的区间上进行读写操作
  • Bidirectional Iterator:可双向移动
  • Random Access Iterator:前三种支持operator++,第四种再加上operator--;这种则涵盖所有指针算术能力,包括p+n, p-n, p[n], p1-p2, p1<p2
这五种迭代器的分类与从属关系(非继承,只是概念和强化)如下:

定义五个classes,代表五种迭代器类型:
struct input_iterator_tag{};struct output_iterator_tag{};struct forward_iterator_tag : public input_iterator_tag{};struct bidirectional_tag : public forward_iterator_tag{};struct random_access_iterator_tag : public bidirectional_iterator_tag{};
这样我们可以做以下测试:
#include <iostream>struct A{};struct B1 : public A {};struct B2 : public A {};template<class I>voidfunc(I& p, A){std::cout << "A version" << std::endl;};template<class I>voidfunc(I& p, B2){std::cout << "B2 version" << std::endl;};int main(){int * p;func(p, A());func(p, B1());func(p, B2());return 0;}
输出结果如下:

我们可以利用这种继承关系,对每一种迭代器类型设计不同的算法。

为了符合规范,任何迭代器都应该提供五个内嵌相应型别,以利于traits萃取,否则便是自别于整个STL架构,可能无法与其他STL组件顺利搭配。STL提供了一个iterator class,如果每个新设计的迭代器都继承自它,就可保证符合STL所需之规范:
template <class Category,           class T,           class Distance = ptrdiff_t,           class Pointer=T*,           class Reference= T&>struct iterator{    typedef Category iterator_category;    typedef T value_type;    typedef Distance difference_type;    typedef Pointer pointer;    typedef Reference reference;};
iterator class 不含任何成员,纯粹是型别定义,所以继承它没有额外负担;由于后三个参数都有默认值,故新的迭代器只需提供前两个参数即可
原创粉丝点击