STL中的traits技术

使用STL中的advance和迭代器类型来讲解trait技术

 

1.迭代器类型

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

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.用迭代器类型和advance的实现来讲解trait技术

STL标准模版库包括:

容器模版

迭代器模版(关联容器 和 算法)

算法模版

工具性模版,如advance

 

这里选择迭代器模版和Advance的实现来讲解trait技术。

 

advance的函数声明

往前移动多少个距离

// advance 函数签名
template <class Iterator, class Distance>
void advance (Iterator& it, Distance n);

 advance的使用

// 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 << '\n';

  return 0;
}

advance内部如何实现——trait技术

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

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

如何取得类型信息呢,容器类型::iterator::iterator_category(如上述2中的定义)

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

 

而trait标准技术是把它放进一个template,并进行一个偏特化版本,来实现迭代器所属类型trait。

1.定义iterator_traits类型

容器类型::iterator::iterator_category 再封装一层

如果是自定义的(比如之前的vector list类型),就直接获取内部定义的迭代器类型
如果是内置类型,就直接给设定成他所属的迭代器类型,用模版偏特化

template<typename IterT> 
struct iterator_traits{ 
    typedef typename IterT::iterator_category iterator_category; 
    // 注意: 这里typename关键字 指示编译器解析 IterT::iterator_category 为一个类型
    ... 
};

2.针对内置指针进行偏特化iterator_traits


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

3.advance内部实现

在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(等等其他类型)
    ... 
}

 

4.advance优化实现——不使用typeid,用函数重载机制

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

不同的类型的迭代器类型调用不同的doAdvance函数。

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

5.iterator_traits中的其他类型定义

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所需的规范,即保证上述的那些trait类型

// 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表现类型信息

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值