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