zoukankan      html  css  js  c++  java
  • STL_源码剖析之三:迭代器与traits

    1、迭代器的的设计思维——stl的关键所在

    无论是泛型思维或STL的实际运用,迭代器都扮演着重要角色。STL的中心思想在于将容器和算法翻开,彼此独立。容器和算法的泛型化,从技术角度看并不困难,C++的class template和function template可以分别达成目标,如何设计出两者之间的粘合剂,才是大难题,而迭代器正是扮演了这个重要角色。以下是容器、算法和迭代器的合作演示,以算法find为例:

    template <class InputIterator, class T>

    InputIterator find(InputIterator first, InputIterator last, const T& value)

    {

        while (first!=last && *fist!=value)

            ++first;

        return first;

    }

    迭代器是一种smart pointer

    迭代器是一种类似指针的对象,指针的各种行为中,最重要的就是解引用(dereference,内容提取)和成员访问(member access);因此迭代器实现最重要的工作就是对operator*和operator->进行重载。

    迭代器相应类型

    在使用迭代器的算法中,我们很可能需要知道迭代器的相应类型:迭代器所指之物的类型。假设算法中要求宣告一个变量,以迭代器所指的类型为类型,该如何是好?

    用函数模板的参数推导机制可以解决这个问题:

    template <class I>

    void func(I iter)

    {

        func_impl(iter,*iter); //func的实现全部移往func_impl

    }

    template<class I, class T>

    void func_impl(I iter, T t)

    {

        T temp; //这里解决了问题

        ...

    }

    可如果迭代器类型要用于函数的返回值就没有办法了,应为函数模板的参数推导只能推导参数,不能推导返回值。

    声明嵌套类型似乎是一个好方法,像这样:

    template <class I>

    struct MyIter

    {

        typedef T value_type;   

    }

    template <class I>

    typename I::value_type func(I iter)

    {

        return *iter;

    }

    看起来不错,但还有一个缺陷,并不是所有的迭代器都是class type,比如原生指针类型;如果不是class type,就无法为它声明嵌套类型。但STL必须接受指针作为一种迭代器。

    2、traits编程技术-stl源码门轮

    traits模板类和partial specilization可以解决上节最后提出的问题。traits,顾名思义,是专门要来提取某个类型特性的类。iterator traits就是用来提取迭代器特性的类。

    value_type是traits的特性之一:

    template <class I>

    struct iterator_traits

    {

        typedef typename I::value_type value_type;

    }

    上面这个traits表明,如果I定义有自己的value_type,那么traits提取的的value_type就是I::value_type。上述那个函数的声明可以改成:

    template <class I>

    typename iterator_traits<I>::value_type  func(I iter)

    {

        return *iter;

    }

    这里除了多了一层间接性,又带来什么好处呢?好处是traits可以由特化版本,我们定义traits的一个偏特化版本如下:

    template <class T>

    struct iterator_traits<T*>

    {

        typedef T value_type;

    }

    这样traits就可以提取原生指针类型所指类型了。

    traits扮演特性提取机的角色,这里所谓迭代器特性,指的就是迭代器的相应类型。若要这个traits能够有效运作,每一个迭代器必须遵守约定,以嵌套类型定义的方式,声明所需的相关类型,谁不遵守这个约定就不能容于stl这个大家庭。

    常用的迭代器相关类型有五种,traits会很忠诚地提取出来:

    template <class I>

    struct iteraotr_traits

    {

    typedef typename I::iterator_category  iterator_category;

    typedef typename I::value_type value_type;

    typedef typename I::deference_type deference_type;

    typedef typename I::pointer pointer ;

    typedef typename I::reference reference;

    }

    迭代器相关类型之一:value_type

        迭代器所指对象的类型

    迭代器相关类型之二:deference_type

        表示两个迭代器之间距离的类型

    迭代器相关类型之三:pointer

        迭代器所指对象的原生指针类型

    迭代器相关类型之四:reference

        迭代器所指对象的原生引用类型

    迭代器相关类型之五:iterator_category

        表明迭代器的类型。

    迭代器的分类

    上节提到的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。

    上述五种分类之间是一种强化关系:Input Iterator和Output Iterator平级,Forward Iterator强化了这两者,Bidirectional Iterator强化了Forward Iterator,Random Access Iterator强化了Bidirectional Iterator。

    从iterator_traits的定义可以推测,最终是以不包含数据的C++类来表示的。

    迭代器的分类可以最大化某些算法的执行效率,以advance函数为例:

    template <class InputIterator, class Distance>

    void advance(InputIterator& i, Distance n)

    该函数将会针对不同的迭代器分类采用不同的策略,RandomAccessIterator直接移动n,对其他类型则移动n步。

    上面这个函数的签名来自stl源码,它的模板参数命名为InputIterator,这其实是stl的一个约定,以算法所能支持的最低阶的迭代器类命名。

    3、SGI STL的私房菜:_type_traits

    traits编程技法很棒,大量运用于stl的实现当中,它利用嵌套内省声明和编译器的模板参数推导功能,补强C++未能提供的关于类型认证方面的能力。

    stl只对迭代器加以规范,制定出iterator_traits这样的东西。SGI把这一技法扩大到迭代器以外的世界,于是就有了_type_traits。iterator_traits负责提取额迭代器的信息,_type_traits负责提取类型特性。

    此处我们关注的类型特性包括:这个类型是否具有non-trivial default ctor;是否具有non-trivial copy ctor;是否具有no-trivial assignment ctor;是否具有non-trivial dtor。这些特性对于对象的创建拷贝移动的效率具有很大的意义。

    _type_traits的定义如下:

    template <class type>

    struct _type_traits

    {

    typedef _false_type has_trivial_default_ctor;

    typedef _false_type has_trivial_copy_ctor;

    typedef _false_type has_trivial_assignment_operator;

    typedef _false_type has_trivial_dtor;

    typedef _false_type is_POD_type;

    }

    模板_type_traits可以接受任何类型的参数,五个typedef将通过以下渠道获得:

    1、一般具现,_type_traits的模板代码采取了最保守的定义,全部为_false_type;

    2、特化版本,<type_traits.h>对C++的标量类型定义了对应的特化版本;

    3、某些编译器会自动为类型生成适当的的特化版本

  • 相关阅读:
    html04
    html03
    html02
    html01
    通过脚本获取form表单的数值而不是submit
    myeclipse自带的数据库查看文件
    如何实现数组和List之间的转换?
    Array和ArrayList有何区别?
    ArrayList和LinkedList的区别是什么?
    如何决定使用HashMap还是TreeMap?
  • 原文地址:https://www.cnblogs.com/longhuihu/p/10423376.html
Copyright © 2011-2022 走看看