stl源码剖析之traits编程技法

stl中充斥着大量的traits编程技法,要想看stl源码,不了解traits肯定是不行的。
一.引入
stl中迭代器类似于指针,可以说指针是一种特殊的迭代器。
假设我们想要写一个func函数,它的输入参数是一个迭代器,并且返回迭代器所指对象的类型,形如下:

template <class T>
struct MyIter{
    typedef T value_type;
    ...
}

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

其中 MyIter是一个迭代器类,而在函数func中, I是迭代器类,it是一个迭代器,value_type表示迭代器所指对象类型,例如I是一个string的迭代器,即string::iterator,则其value_type即为string。
将MyIter应用于func函数:

MyIter<int> it(new int(8));  //it为MyIter类型的迭代器
cout<<func(it);   //输出为8

目前这个函数只适用于拥有value_type的class类,并不适用于原生指针的情况,比如我想在I为int *的情况下使用,这时返回的数据应该是一个int型。
如何做呢?template partial specialization可以做到,首先看一下stl的traits。

二.traits
traits的意思就是”特性”,traits编程技巧有点类似于一个萃取机,可以萃取出其中的特性。比如stl中的iterator_traits就可以萃取出迭代器的特性,value_type就是迭代器的其中一个特性,它可以萃取出迭代器所指对象的类型。下面这个iterator_traits可以萃取出迭代器的value_type

template <class _Iterator>
struct iterator_traits{
    typedef typename _Iterator::value_type value_type;
};

定义了iterator_traits后,之前的func函数可以写成如下形式:

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

目前这个func函数还是不支持原生指针(如int*),这时可以使用前面提到的template partial specialization(偏特化),如下增加iterator_traits针对T 以及const T 的偏特化:

//针对原生指针T*的偏特化
template <class T>
struct iterator_traits<T*>{
    typedef T value_type;
}

//针对const T*的偏特化
template <class T>
struct iterator_traits<const T*>{
    typedef T value_type;
}

这样,func函数输入参数为int *时,返回值即为int型。

三.stl中iterator_traits其它特性
stl中定义的iterator_traits可以萃取出五种特性,即value type(迭代器所指对象类型),difference type(两个迭代器之间的距离),reference type(迭代器所指对象的内容的引用),pointer type(迭代器所指对象的指针),iterator_category(迭代器类型)。
定义如下,第五个iterator_category下面单独介绍。

template <class I>
struct iterator_traits{
    typedef typename I::value_type      value_type;     //类型
    typedef typename I::pointer         pointer;         //指针
    typedef typename I::reference       reference;       //引用
    typedef typename I::difference_type difference_type; //两个迭代器之间的距离
    typedef typename I::iterator_category iterator_category; //迭代器类型
};

//针对原生指针的偏特化
template <class I>
struct iterator_traits<T*>{
    typedef I   value_type;
    typedef I*  pointer;
    typedef I&  reference;
    typedef ptrdiff_t difference_type;
    typedef random_access_iterator_type iterator_category;//迭代器类型(随意移动型)
};

//针对const *的偏特化
template <class I>
struct iterator_traits<const I*>{
    typedef I   value_type;
    typedef const I*  pointer;
    typedef const I&  reference;
    typedef ptrdiff_t difference_type;
    typedef random_access_iterator_type iterator_category;
};

四.iterator_category
根据移动特性和施行操作,迭代器可被分为五类:
1.Input Iterator:这种迭代器所指的对象不有允许外界改变。只读
2.Output Iterator:只写
3.Forward Iterator:允许写入,前向移动。(前三种支持operator++)
4.Bidirectional Iterator:可双向移动。(支持operator++,operator–)
5.Random Access Iterator:任意移动(支持指针任意移动,涵盖所有指针算术能力,包括p+n,p-n,p[n],p1-p2等)

在stl泛型算法中,有一个advance函数,形如
template <class InputIterator, class Distance>
void advance (InputIterator& it, Distance n);

表示将迭代器it移动距离n,现在考虑它的实现。
迭代器移动特性不同,则相应的advance函数具体实现会不同,例如,上面所述的前三种迭代器只支持一步一步的前向移动,但第五种迭代器却可以支持任意移动。
定义五个类分别表示这五种迭代器。

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 InputIterator, class Distance>
inline void __advance(InputIterator& i,Distance n,input_iterator_tag)
//第三个参数声明为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());//第三个参数为input_iterator_tag类的默认构造函数,传给__advance函数一个临时对象
}
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;
}

其实,第二个函数,ForwardIterator版本的__advance函数可以去掉,因为forward_iterator_tag和input_iterator_tag是继承关系,具体可以往下看。

template <class InputIterator, class Distance>
inline void advance(InputIterator& i,Distance n)
{
    __advance(i,n,iterator_traits<InputIterator>::iterator_category());//第三个参数是一个临时对象
}

iterator_category()函数实现如下:

template <class I>
inline typename iterator_traits<I>::iterator_category
iterator_category(const I&)
{
    typedef typename iterator_traits<I>::iterator_category  category;
    return category();
}

函数iterator_category()返回一个iterator_category的对象,iterator_category是input_iterator_tag,forward_iterator_tag,bidirectional_iterator_tag,random_access_iterator_tag四个struct类之一。iterator_category()函数调用了这四个类之一的构造函数以产生一个临时对象,并传递给函数__advance()的第三个参数。

五.iterator类
为符合stl规范,任何迭代器都应该定义五种相应特性,以利于traits萃取。stl定义了一个iterators class,任何新设计的迭代器继承自它,都将符合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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值