【STL源码剖析】__iterator_traits技法

来源:互联网 发布:粤知一二骑马过海 编辑:程序博客网 时间:2024/05/17 19:19

__iterator_traits技法用于模板编程,STL所有算法都是基于模板实现的。

先看下面是一个例子:

template <class I, class T>void func_impl(I iter, T t){// ...}template<class I>inline void func(I iter){func_impl(iter, *iter);// ...}int main(){int i;func(&i);return 0;}

这里利用了function template的参数推导机制。

但是,想象一下,“template参数推导技术”只是推出参数,无法推导返回值,怎么办?我们可以在迭代器里内嵌一个型别声明。如下:

template<class T>struct MyIter{typedef T value_type;// ....};template<class I>typename I::value_type func(T iter){return *iter;}

通过在迭代器类里面嵌入一个声明,解决了推导返回值的问题(注意:上面要用typename,因为MyIter是个模板,编译器对T模板参数一无所知)!

但问题又来了,并不是所有类型都是calss type,所以无法为它定义内嵌型别,template partial specialization(模板偏特化)可以做到,但要用另一个模板作为中间层进而偏特化(这里就是__iterator_traits技法了),如下:

//__iterator_traitstemplate<class I>struct iterator_traits{typedef typename I::value_type value_type;};//__iterator_traits偏特化版本,准确接受内置指针类型template<class I>struct iterator_traits<T*>{typedef T value_type;};//接受指向const类型数据的指针template<class I>struct iterator_traits<const T*>{typedef T value_type;};

上面“无法推导返回值类型”的问题可以这样解决了:

template<class T>struct MyIter{typedef T value_type;// ....};template<class I>typename iterator_traits<I>::value_type func(I iter){return *iter;}

这就是__iterator_traits编程技法。

不过,在《stl源码剖析》是在2002出版的,我看了下只有第一版,那时候还没有C++11,在C++11的新语法下,我们其实可以这样解决“无法推导返回值”的问题,那就是利用auto、decltype、尾置返回语法的性质

template<class T>struct MyIter{typedef T value_type;// ....};template<class I>auto func(I iter) -> typename remove_reference<decltype(*iter)>::type{return *iter;}
因为在推导返回值时,参数类型已有函数实参推断出来了,再用尾置返回,搞定。同时,要用decltype(*iter)得到的是引用类型,故需要remove_reference。


所以,STL里的算法模板都是这样实现,如果我们要设计与STL水乳交融的类,那就要符合STL的一些基本要求了。

1.设计类

2.设计类的迭代器

迭代器要满足STL的要求,那么你的迭代器就能用在STL算法。


STL有5中基本型别,它的特性萃取机这样定义的:

template<class I>struct iterator_traits{typedef typename I::iterator_category iterator_category;typedef typename I::value_type value_type;typedef typename I::difference_type difference_type;typedef typename I::pointer pointer;typedef typename I:;reference reference;}
当然了,还有针对pointer和pointer-to-const者设计特例化版本,这些在STL内部已经实现了我们需要做的是,设计一个迭代器,里面要嵌入5中型别的定义

iterator_category有什么用?它是用来判别这个迭代器(或许是指针)是什么类型的,根据移动特性与施行操作,迭代器被分为5类:

①Iuput iterator,只读

②Output iterator,只写

③Foreard iterator,可读写

④Bidirection iterator,可双向移动

⑤Random Access iterator,前四种提供基本操作(operator++,第四种在加上operator--),这种包含所有指针算术能力(p+n,p-n,p[n],p1<p2...)

为了分别五种型别,STL内部定义了5个类来区分:

struct input_iterator_tag{};struct output_iteraotr_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{};

所以要设计能使用STL算法的迭代器,就要内嵌5种型别的定义,我们可以继承stl定义的iterator:

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

那么STL算法是如何运用这些机制的呢,一个例子如下:

template<class InputIterator, class Distance>inline void __advance(InputIterator &i, Distance n, 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());}template<class BidirectionIterator, class Distance>inline void __advance(BidirectionIterator &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;}template<class Iterator, class Distance>inline void advance(Iterator &i, Distance n){__advance(i, n, iterator_traits<Iterator>::iterator_category());}

上面,advance对我们可见,通过iterator_category来区分了不同的迭代器,以上就是__iterator_traits的全部了。





0 0
原创粉丝点击