• 使用traits技术表现迭代器类型 iterator_category


    1.STL的迭代器类型标识,和容器中的迭代器类型。

    2.使用trait技术实现stl的advance功能。

    3.迭代器trait中的其他类型定义。 

    1.STL的迭代器类型标识,和容器中的迭代器类型。

     熟悉的迭代器类型,输入、输出、前向、双向、随机。

    c++标准程序库分别提供专属的卷标结构(tag struct),一个空的结构体来标志具体是哪种类型迭代器。如下:

            //    ITERATOR STUFF (from <iterator>)
            // ITERATOR TAGS (from <iterator>)
    struct input_iterator_tag
        {    // identifying tag for input iterators   
        };
    
    struct output_iterator_tag
        {    // identifying tag for output iterators
        };
    
    struct forward_iterator_tag
        : input_iterator_tag, output_iterator_tag
        {    // identifying tag for forward iterators  这些继承关系有有效的IS-A
        };
    
    struct bidirectional_iterator_tag
        : forward_iterator_tag
        {    // identifying tag for bidirectional iterators
        };
    
    struct random_access_iterator_tag
        : bidirectional_iterator_tag
        {    // identifying tag for random-access iterators
        };

    而在每个容器中,就使用上述的tag struct标识自自身迭代器的类型,大概的思路是下面的样子,但真正源码实现复杂多。

    // vector 容器 随机迭代器。list 容器 双向迭代器。
    template<...> class vector{ public: class iterator{ public: typedef random_access_iterator_tag iterator_category;
         // 类型定义: vector<T>::iterator::iterator_category 就是 random_access_iterator_tag
         // 即类型里面还有一个类型, 而这个类型仅仅是用来标识 这个类是属于哪个类型的。 }; }; template
    <...> class list{ public: class iterator{ public: typedef bidirectional_iterator_tag iterator_category; }; };

     

    2.使用trait技术实现stl的advance功能。

    STL标准模版库={容器templates,迭代器templates(关联容器 和 算法),算法templates,工具性template如advance}。这里选择迭代器模版和Advance的实现来讲解trait技术。

     

    先看看advance的用法:

    // advance 函数签名
    std::advance template
    <class Iterator, class Distance> void advance (Iterator& it, Distance n);
    // advance example
    #include <iostream>     // std::cout
    #include <iterator>     // std::advance
    #include <list>         // std::list
    
    int main () {
      std::list<int> mylist;
      for (int i=0; i<10; i++) mylist.push_back (i*10);
    
      std::list<int>::iterator it = mylist.begin();
    
      std::advance (it,5); 
    
      std::cout << "The sixth element in mylist is: " << *it << '
    ';
    
      return 0;
    }

    advance内部操作时候,需要知道advance的迭代器类型,看有哪些可用操作,比如随机访问器可以直接+= -=操作,前向仅支持++,输入输出均不支持,例子中的list属于双向,支持++,--。

    所以在advance内部需要在取得某种类型信息,即迭代器的类型,进行不同的实现。

     

    如何取得类型信息呢,在1中其实我们已经定义了迭代器的类型,可以通过 ”容器类型::iterator::iterator_category“来获取类型信息。

    但如果要支持内置类型,比如指针是一种随机迭代器类型,那么类型信息就不能放在类型内了,意味着类型内的嵌套类的方式不能工作,所以类型的信息必须位于类型自身之外。

     

    trait标准技术是把它放进一个template,并进行一个偏特化版本,来实现迭代器所属类型trait。trait特性的意思 就有有关迭代器的相关特性。

    // 即再封装一层,如果是用户自定义的,就直接获取内部定义的迭代器类型
    // 如果是内置类型,就直接给设定成他所属的迭代器类型,用模版偏特化
    // iterator_traits 
    template<typename IterT> 
    struct iterator_traits{ 
        typedef typename IterT::iterator_category iterator_category; 
        // 注意: 这里typename关键字 指示编译器解析 IterT::iterator_category 为一个类型
        ... 
    };
    
    template<typename IterT>        //template 偏特化,针对内置指针 
    struct iterator_traits<IterT*>{ 
        typedef random_access_iterator_tag iterator_category; 
    };

    template<typename IterT>       
    struct iterator_traits<const IterT*>{ 
        typedef random_access_iterator_tag iterator_category; 
    };
     

     

    在advance的用法中,就可以使用 iterator_traits 类来判断是什么类型

    // advance运行时确定使用哪种迭代器类型版本
    template<typename IterT, typename DistT> void advance(IterT& iter, DistT d) { if (typeid(typename std::iterator_traits<IterT>::iterator_category) == typeid(std::random_access_iterator_tag)) { iter += d; } else if(前向迭代器类型) { if (d < 0){throw std::out_of_range("Negative distance");} while (d--) ++iter; } else if(等等其他类型) ... }

     

    使用重载函数的机制,在编译器就确定调用哪个迭代器类型的advance,以提高运行时效率。

    // advance编译器确定使用哪种迭代器类型版本
    template<typename IterT, typename DistT> 
    void advance(IterT& iter, DistT d) 
    { 
        doAdvance(iter, d, typename std::iterator_traits<IterT>::iterator_category()); 
    }
    // 比如 双向迭代器的函数 。重载doAdvance,实现不同的迭代器类型的具体操作。
    // 可以看到迭代器类型仅仅是个重载的作用,使得重载机制得以运行,都不需要变量名。
    void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag)          
    {                                                                                                               
        if (d >= 0) {while (d--) ++iter;} 
        else {while(d++) --iter;} 
    } 

     

    3.迭代器trait中的其他类型定义

    template<class IterT>
    struct iterator_traits{
        typedef typename IterT::iterator_category iterator_category;  // 迭代器的类型所属
        typedef typename IterT::value_type value_type;            // 迭代器所指对象的类型
        typedef typename IterT::difference_type difference_type;     // 迭代器之间的距离
     
        typedef typename IterT::pointer pointer;              // 迭代器所指内容的地址
        typedef typename IterT::reference reference;            // 迭代器所指之内容
    };
    
    template<typename IterT>        //template 偏特化,针对内置指针 
    struct iterator_traits<IterT*>{ 
        typedef random_access_iterator_tag iterator_category; 
        typedef IterT value_type; 
        typedef ptrdiff_t difference_type ; 
    typedef IterT
    * pointer ; typedef IterT& reference ; };

    另外,STL提供了一个 iterator类,如果每个新设计的迭代器都继承他,可保证符合STL所需的规范。

    // TEMPLATE CLASS iterator
    template<class _Category,
        class _Ty,
        class _Diff = ptrdiff_t,
        class _Pointer = _Ty *,
        class _Reference = _Ty&>
        struct iterator
        {    // base type for iterator classes
        typedef _Category iterator_category;
        typedef _Ty value_type;
        typedef _Diff difference_type;
    
        typedef _Pointer pointer;
        typedef _Reference reference;
     };

    整理自 effective C++ 条款 47:使用traits classes表现类型信息

  • 相关阅读:
    Android Lint简介
    免费HTTP数据抓包Fiddler2[4.6.1.2]以及显示中文包内容的方法
    IE6、7下bug
    图表插件
    学习:使用svg
    jQuery Transit
    jQuery基础学习笔记(1)
    HTTP协议详解学习
    html5学习笔记
    html释疑
  • 原文地址:https://www.cnblogs.com/fulina/p/7058288.html
Copyright © 2020-2023  润新知