迭代器(Iterator)概念与traits编程技法

来源:互联网 发布:博湃养车 数据 编辑:程序博客网 时间:2024/04/29 06:38
Iterator是一种抽象的设计概念《Design Patterns》其中对于iterator模式定义如下:提供一种方法,使之能够依序讯访某个聚合物(容器)所包含的各个元素而又无需暴露该聚合物的内部表达方式。
STL的中心思想在于:将数据容器(containers)和算法(algorithms)分开彼此独立设计,最后再以一贴粘合剂将其撮合。这个粘合剂就是Iterator。
迭代器相应型别(associated types)
什么是型别?迭代器所指的对象类型就是一种型别。如何获得?
// 尝试一
#include"stdio.h"#include"stdlib.h"template<typename I, typename T>void func_impl(I iter, T t) {    T tmp;//可能其中的算法需要使用一个中间变量    //....  这里做func原本该做的事情}template<typename I>void func(I iter) {    func_impl(iter, *iter);}int main() {    int i;    func(&i);}


上面的代码展现了传入Iterator, 可能在某个算法中需要知道iterator所指对象的类型,采用function template的方式解决,一旦被调用,编译器会进行参数推到知道T的类型。当然迭代器的相应型别不知这一种。
迭代器所指对象的类型成为迭代器的value type。上述方法并不能解决所有的情况,比如value type必须作为函数的传回值,那就不行了,因为“template参数推导机制”推而导之的只是参数,无法推导函数的回返值型别。
补充:对于上述推导机制的理解(只能从参数推导出来,而不能从返回值推导出来)
template<class T>
T fun(T i) {
    T a = i;
    return i;
}
int i = 0;
fun(i);
上述代码是正确的,但是并不是说可以推导函数的回反值类型,因为这个typename T,是从参数推导出来的,不是从返回值推导出来的,如下代码表达的就是这个意思。
template<class T>
T fun() {
    T a
    return a;

int i = fun();// error  当然在模板参数列表中添加个返回值类型参数,调用的时候传递进去使用,fun<int>()这样当然是没问题的

//尝试二#include <iostream>using namespace std;template<typename T>struct MyIter {    typedef T value_type;    T* ptr;    MyIter(T* p = 0):ptr(p){}    T& operator*() const { return *ptr; };};template<typename I>typename I::value_type func(I iter) { // 使用typename是告诉编译器这是型别,否则编译器不清楚到底是成员变量还是什么。    return *iter;}int main() {    MyIter<int> i(new int(8));    cout << func(i) << endl;}

尝试二看上去很好的解决了问题,但是如上代码存在一个问题,就是如果T为非class type就无法使用内嵌类别,比如说原生指针。为了解决这个问题引入特殊化处理(template partial specialization, 偏特化)
partial specialization的定义:“针对(任何)template参数更进一步的条件限制所设计出来的一个特化版本”《泛化思想》
看个例子就很清楚这个定义了:
template<typename T>
class C {...} // 这个泛化版本允许接受T为任何型别
另一个形式的特化版本
template<typename T>
class C<T*> {...} //  这个特化版本只允许T为原生指针,也就是着T为原生指针是T为任何指针的进一步条件限制。

//尝试三 引入
template <class I>
struct iterator_traits {
    typedef typename I::value_type value_type;
};
template <class T>
struct iterator_traits<T*> {
    typedef T value_type;
}; //偏特化版,原生指针

于是原生指针int* 虽然不是class type通过:iterator_traits<int*>::value_type很容易获得原生指针型别
int则可以通过iterator_traits<int>::value_type获得。
同时对于指向const对象的指针可以偏特化
...iterator_traits<const T*>...
除了value type迭代器最常用的还有四种,更value type的获取类似,可以通过iterator_traits的方法
型别二:difference type:
表现的是两个迭代器之间的距离。(注释,原生指针的偏特化版本difference type 是c++内嵌类型ptrdiff_t)
型别三四:reference type和pointer type
template <typename T>
struct MyList{ ...};
则T& 和 T*分别是MyList的reference type 和 pointer type;
型别五:iterator_category
先介绍,迭代器的五类:
Input Iterator: 只读
Output Iterator:唯写
Forward Iterator:允许“写入型”算法,(如replace)在此种迭代器所形成的区间上进行读写操作。
Bidrectional Iterator: 可双向移动,某些算法需要逆向走访某个迭代器区间(如逆向拷贝某范围的元素),可以使用这类迭代器
Random Access Iterator: 前四种迭代器都只供应一部分指针算术能力(前三支持operator++, 第四种加上operator--),第五种则涵盖了所有的算术能力,包括p+n,p-n;
继承关系,Forward继承Input ,Output,Bidrectional继承Forward, Random继承Bidrecional。假如一个算法可接受Forward你将Random传入,必然是可以的,但是并不代表最佳。

如果traits有能力萃取出迭代器的种类,我们便可利用这个“迭代器 类型”相应型别作为advanced()等函数的第三参数。这个相应型别一定必须是一个class type,不能只是数值号码类的东西,因为编译器需依赖它(一个型别)来进行重载决议(overloaded resolution)。

五种迭代器类型:

//五个作为标记用的型别(tag types)

struct input_iterator_tag{ } ;

structoutput_iterator_tag { } ;

structforward_iterator_tag : public input_iterator_tag { } ;

structbidirectional_iterator_tag : public forward_iterator_tag { } ;

structrandom_access_iterator_tag : public bidirectional_iterator_tag { } ;

template<class InputIterator,class Distance>  inline void __advance(InputIterator &i,Distance n, input_iterator_tag)  {      //单向,逐一前进      while (n--) ++i ;  }      template<class ForwardIterator,class Distance>  inline void __advance(ForwardIterator &i,Distance n,forward_iterator_tag)   {      __advance(i,n,input_iterator_tag()) ; //由于正向迭代器也是输入迭代器  }      template<class BidiectionalIterator,class Distance>  inline void __advance(BidiectionalIterator &i,Distance n,bidirectional_iterator_tag)  {      //双向,逐一前进         if(n >= 0)      {          while(n--) ++i ;      }      else              while(n++) --i ;  }    template<class RandomAccessIterator,class Distance>  inline void __advance(RandomAccessIterator &i , Distance n ,random_access_iterator_tag)  {      //双向,跳跃前进      i += n ;      }  

上层对外开放接口

//声明为InputIterator的原因是,命名规则以算法所能接受之最低阶迭代器类型,来为其迭代器型别参数命名。  template<class InputIterator,class Distance>   inline void advance(InputIterator &i , Distance n)   {      //最后一个参数生成一个临时对象,触发重载机制,然后调用相应的__advance函数      __advance(i,n,iterator_traits<InputIterator>::iterator_category()) ;   }

小结上述描述的相应型别

template<class I>  struct iterator_traits{  typedef typename I::iterator_category  iterator_category ;  typedef typename I::value_type  value_type ;  typedef typename I::difference_type difference_type ;  typedef typename I::pointer pointer ;  typedef typename I::reference reference ;  }  // 仅泛化版本,当然还有针对原生指针偏特化版本,pointer to const偏特化版本,这里就不再赘述。

为了与stl顺利搭配,任何迭代器都需要提供五个内嵌相应型别,STL提供了一个iterator class(std::iterator)如下,如果每个新设计的迭代器继承自它,就保证可以符合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 ; };


0 0
原创粉丝点击