• 迭代器概念与traits编程技法


      迭代器是一种类似于指针的对象。

      在算法运用迭代器的时候,可能会用到其相应的型别,为了获得迭代器所指对象的型别,有以下几种方法

    function template的参数推导机制

    template <class I,class T>
    void func_impl(I iter,T t)
    {
        T tmp;    //T就是迭代器所指之物的型别
        //。。。完成func()应该做的全部工作。
    }
     
    template <class I>
    void func(I iter)
    {
        func_impl(iter,*iter);    //func的工作全部移往func_impl
    }
     
    int main()
    {
        int i=100;
        func(&i);
    }

      但是template参数推导机制只是推导参数而无法推导返回值,所以我们可以声明为内嵌型别

    内嵌型别

    Template<class T>
    Struct MyIter
    {
        Typedef T value_type;//内嵌型别
        T *ptr; //底层指针
        MyIter(T *p=0) : ptr(p){}
        T& operator*() const {return *ptr;}
    };
    Template<class I>
    Typename I::value_type func(I ite){return *ite;}
    //..
    MyIter<int> ite(new int(8));
    cout<<func(ite);

      但是并非所有的指针都为class type,还必须接受原生指针作为迭代器,所以要用偏特化——如果class type有一个以上的template参数,我们可以针对其中部分template参数做特化工作,也就是泛化版本中的一些特化。

    template <class T>
    class C{};//泛化版本,接受T为任何类型
    
    template <class T>
    class C<T*>{};//特化版本,接受T为原生指针类型

    Traits编程技法

      萃取各个迭代器的特性,也即是迭代器相应的型别。自以内嵌型别定义的方式定义出相应的型别。iterator_traits是专门用来萃取迭代器的特性。如果I定义有自己的value_type那么通过traits萃取出来的就是I::value_type。

      上面的func可以写作

    template <class T>
    typename iterator_traits<I>::value_type func(I ite)
    {
        return *ite;
    }

      traits的好处是可以拥有特化版本,使得原生指针也有自己的value_type,不论是面对迭代器还是原生指针都能萃取出自己正确的value_type。

    template <class T>
    struct iterator_traits<T*>
    {
        typedef T value_type;
    };
    
    template <class T>
    struct iterator_traits<const T*>//但是对于指向常数对象的指针,pointer-to-const我们应该让其返回non-const。
    {
        typedef T value_type;
    };

    //用于traits出迭代其所指对象的型别
    template <class Iterator>
    struct iterator_traits
    {
      // 迭代器类型, STL提供五种迭代器
      typedef typename Iterator::iterator_category iterator_category;
    
      // 迭代器所指对象的型别
      // 如果想与STL算法兼容, 那么在类内需要提供value_type定义
      typedef typename Iterator::value_type        value_type;
    
      // 这个是用于处理两个迭代器间距离的类型
      typedef typename Iterator::difference_type   difference_type;
    
      // 直接指向对象的原生指针类型
      typedef typename Iterator::pointer           pointer;
    
      // 这个是对象的引用类型
      typedef typename Iterator::reference         reference;
    };
    
    //针对指针提供特化版本
    template <class T>
    struct iterator_traits<T*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef T                          value_type;
      typedef ptrdiff_t                  difference_type;
      typedef T*                         pointer;
      typedef T&                         reference;
    };
    
    //针对指向常对象的指针提供特化
    template <class T>
    struct iterator_traits<const T*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef T                          value_type;
      typedef ptrdiff_t                  difference_type;
      typedef const T*                   pointer;
      typedef const T&                   reference;
    };
    
    //返回迭代器类别
    template <class Iterator>
    inline typename iterator_traits<Iterator>::iterator_category
    iterator_category(const Iterator&)
    {
      typedef typename iterator_traits<Iterator>::iterator_category category;
      return category();
    }
    
    //返回表示迭代器距离的类型
    template <class Iterator>
    inline typename iterator_traits<Iterator>::difference_type*
    distance_type(const Iterator&)
    {
      return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
    }
    
    //返回迭代器所指对象的类型
    template <class Iterator>
    inline typename iterator_traits<Iterator>::value_type*
    value_type(const Iterator&)
    {
      return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
    }

      当我们对一个mutable iterator进行提领操作时,获得的不应该是一个右值而应该是一个左值,因为右值不允许赋值操作左值才允许。

        int* p1=new int(2);
        const int* p2=new int(6);
        *p1=6;
        *p2=9;//error

      c++中函数如果要传回左值,都是以by reference传递。所以当p是mutable iterator时,如果其value_type是T,那么*p应是T&;如果p是const iterator,value_type是T,那么*p是const T&。

     

      假设算法参数为forward iterator,当我们传递进去bidirectional iterator时,也能正常运作,所以我们要把算法设计的尽可能使用多种迭代器。

      但是每种迭代器所对应的的最优算法操作并不同,所以我们在统一的算法接口中要根据传入得迭代器不同来进行不同的计算,为了使编译期就确定使用哪种算法,我们可以使用函数重载,所以我们要一个型别已确定的参数,使得重载机制能够运行。这个型别必须是class type,不能是数值类型的,因为编译器需依赖他进行重载。定义五个class代表五种迭代器类型:

    /**
     * 用于标记迭代器类型
     */
    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 {};

       如下使用:

    template <class Iterator>
    inline typename iterator_traits<Iterator>::iterator_category
    iterator_category(const Iterator&)
    {
      typedef typename iterator_traits<Iterator>::iterator_category category;
      return category();
    }
    
    template <class InputIterator,class Distance>
    inline void advance(InputIterator& i,Distance n)
    {
        __advance(i,n,iterator_traits<InputIterator>::iterator_category());//iterator_category()产生一个临时对象
    }

      任何一个迭代器应该在所属类型中,最强化的那个,比如int*既属于Random Access Iterator又属于Bidirectional Iterator、Input Iterator、Forward Iterator,但是其因该属于random_access_iterator。

      算法设计应该能接受最低阶类型迭代器

      stl提供一个iterator class,每个设计的新迭代器都应该继承它,它不包含任何成员,只是单纯的型别定义。后三个都有默认值,在使用是只需提供前两个之即可。

    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;
    };

      使用如下:

    template <class Item>
    class MyIter:public std::iterator<std::forward_iterator_tag,Item>
    {
        //...
    };

    总结:

      设计适当的型别,是迭代器的责任,设计适当的迭代器是容器的责任。因为只有容器本身才知道怎样设计迭代器来遍历自己。

    __type_traits

      iterator_traits负责萃取迭代器的特性(型别),__type_traits负责萃取型别的特性。型别特性指的是:这个型别是否具有non-trivial default ctor?non-trivial copy ctor?non-trivial assignment ctor?non-trivial dtor?如果是否定的,则可以在拷贝构造……采取最右措施。

      根据之前对于iterator_traits的学习,我们想必是希望与其有相同的形式可以使用:

    __type_traits<T>::has_trivial_default_constructor;
    __type_traits<T>::has_trivial_copy_constructor;
    __type_traits<T>::has_trivial_assignment_operator;
    __type_traits<T>::has_trivial_destructor;
    __type_traits<T>::is_POD_type;

      且我们希望他们返回的是一个对象(或者说我们需要的是它的型别用以判断)而不是单纯的bool类型的值,可以帮助我们实现重载

    struct __true_type {
    };
    struct __false_type {
    };

      STL对最初的__type_traits是这样处理的

    template <class type>
    struct __type_traits {
       typedef __true_type    this_dummy_member_must_be_first;
        //上面这个先不管
       typedef __false_type   has_trivial_default_constructor;
       typedef __false_type   has_trivial_copy_constructor;
       typedef __false_type   has_trivial_assignment_operator;
       typedef __false_type   has_trivial_destructor;
       typedef __false_type    is_POD_type;
    };

      使用:

    //首先通过value_type(其实就是通过iterator_traits)得到了first类型中的value_type,即first的型别
    template <class ForwardIterator, class Size, class T>
    inline ForwardIteratoruninitialized_fill_n(ForwardIterator first, Size n,
                                                const T& x) {
      return __uninitialized_fill_n(first, n, x, value_type(first));
    }
    
    //再通过__type_traits得到该型别是否是POD类型
    template <class ForwardIterator, class Size, class T, class T1>
    inline ForwardIterator__uninitialized_fill_n(ForwardIterator first, Size n,
                                                  const T& x, T1*) {
      typedef typename __type_traits<T1>::is_POD_type is_POD;
      return __uninitialized_fill_n_aux(first, n, x, is_POD());
                                        
    }
    
    //如果正是POD类型的话,只要简单的操作
    template <class ForwardIterator, class Size, class T>
    inline ForwardIterator
    __uninitialized_fill_n_aux(ForwardIteratorfirst, Size n,
                               const T& x,__true_type) {
      return fill_n(first, n, x);
    }
    template <class OutputIterator, class Size, class T>
    OutputIteratorfill_n(OutputIterator first, Size n, const T& value) {
      for ( ; n > 0; --n, ++first)
        *first = value;
      return first;
    }
    
    
    //如果不是POD类型,那么就需要老老实实的一个一个在未初始化的内存中调用构造函数了!
    template <class ForwardIterator, class Size, class T>
    ForwardIterator
    __uninitialized_fill_n_aux(ForwardIteratorfirst, Size n,
                               const T& x,__false_type) {
      ForwardIterator cur = first;
      __STL_TRY {
        for ( ; n > 0; --n, ++cur)
          construct(&*cur, x);
        return cur;
      }
      __STL_UNWIND(destroy(first, cur));
    }

       iterator_traits的用法

    #include <iostream>
    #include <typeinfo>
    #include <iterator>
    #include <vector>
    using namespace std;
    
    class A
    {
        public:
            A(long long i=10)
            {
                k=i;
            }
        private:
            long long k;
    };
    
    template<class it>//pointer and value_type and reference
    void fun(it)
    {
        typedef typename iterator_traits<it>::pointer pointer;
           cout<<"pointer:"<<typeid(pointer).name()<<endl;
    
        typedef typename iterator_traits<it>::value_type value_type;
        cout<<"value_type:"<<typeid(value_type).name()<<endl;
    
        typedef typename iterator_traits<it>::reference reference;
        cout<<"reference:"<<typeid(reference).name()<<endl;
    }
    
    template<class it>//iterator_category and difference_type
    void func(it t1,it t2)
    {
        typedef typename iterator_traits<it>::iterator_category  iterator_category;
        cout<<"iterator_category:"<<typeid(iterator_category).name()<<endl;
    
        typedef typename iterator_traits<it>::difference_type difference_type;
        cout<<"difference_type:"<<typeid(difference_type).name()<<endl;
    }
    
    int main()
    {
        A *a=new A(6);
        fun(a);
    
        vector<int> vec(10);
        func(vec.begin(),vec.end());
        return 0;
    }
  • 相关阅读:
    go语言中文网
    理解Golang包导入
    如何保证对象的唯一性
    模拟java.util.Collection一些简单的用法
    静态代码块,构造代码块,局部代码块演示
    java中paint方法和paintComponent方法的不同
    java中异常注意问题(发生在多态是的异常问题)
    java中异常注意的细节2
    java中异常注意的细节1
    java中匿名类的注意细节
  • 原文地址:https://www.cnblogs.com/tianzeng/p/12552888.html
Copyright © 2020-2023  润新知