迭代器(iterators)与traits编程技巧
2017-06-17 17:28
495 查看
1 迭代器基础
迭代器(iterator):在不暴露容器内部的前提下,能依序访问容器的每个元素。迭代器是STL的关键,STL的中心思想是:将容器与算法分开,彼此独立,然后用粘合剂进行关联,迭代器就是这个粘合剂。
通过平时对STL的应用可以发现,每个容器都有自己的迭代器,例如:vector<int>::iterator ;list<int>::iterator等,迭代器总是依附于容器的。为了将实现细节封装,STL将迭代器的开发交给了容器的设计者。所以,每种STL中每种容器都有自己专属的迭代器。
2 traits技巧与容器的内嵌类型
上篇博客讲配置器时,有提过traits编程技巧的一个应用,_type_traits。traits技巧在STL中应用的非常广泛,这里通过例子再次加深对traits技巧的理解。问题1:
如果某一个算法,传入迭代器参数,需要用到“迭代器所指对象”的类型,怎么办?
//func是对外接口 template<typename I> void func(I it) { //将主要工作交给impFunc完成 //关键在于将it所指对象传递过去 //通过模板"参数推导"功能可获得迭代器所指对象的类型 implFunc(it,*it); } template<typename I ,typename T> void implFunc(I it,T val) { T tmp;//可以构造T类型的对象了 //主要工作在这里完成 }问题2:
模板只能推导参数类型,不能推导返回类型。如果上述算法需要返回 迭代器所指对象的类型,又该怎么办??
这里就要用到类模板的内嵌类型了!!
template<typename T> class MyIter { public: typedef T value_type;//声明内嵌类型(nest type) T *ptr; //..... } template<truename I> typename I::value_type//返回类型 func(I it) { //实际工作 }
问题3:
对于原生指针,例如int *,char *,double *等。声明内嵌类型是实现不了的,那么上面的func()函数对原生指针就运行不了了。如果想让func()在支持迭代器的同时也支持原生指针,又该怎么办呢???
这里就得用到traits编程技巧了!!!
//iterator_traits的一般化 template<typename I> struct iterator_traits { typedef typename I::value_type value_type; } //针对 I为原生指针 iterator_traits的特化版本 template<typename T> struct iterator_traits(T *) { //对于原生指针 value_type 就是T typedef T value_type; } //所以func()函数就可以这样写 template<typename I> typename iterator_traits<I>::value_type func(I it) { //这样不管I是迭代器类型,还是原生指针 //该函数都能正常工作 return *ite; }
STL为了让iterator_traits能正常运行,要求每个迭代器都必须定义自己的内嵌类型。
3 迭代器的相应类型
除了上面value_type外,迭代器还有四个相应类型value type 、difference type 、pointer、reference、iterator catagoly>。
value type:表示迭代器所指对象的类型。
difference type :表示两个迭代器之间的距离。
pointer :指向迭代器操作对象的指针。
reference :迭代器所指对象的引用类型。
iterator catagoly:表示迭代器的类型。下面会详细介绍。
和上述的value type一样,每种容器的迭代器都必须定义这五种类型。然后,iterator_traits就可以如下定义:
//iterator_traits的默认版本 template<typename I> class iterator_traits { //每种容器的迭代器都必须定义value_type,value_type,pointer,reference,iterator_category typedef typename I::value_type value_type; typedef typename I::difference_type value_type; typedef typename I::pointer pointer; typedef typename I::reference reference; typedef typename I::iterator_category iterator_category; } //针对 I为 原始指针的特化版本 template<typename T> class iterator_traits<T *> { typedef T value_type; typedef ptrdiff_t difference_type;//build-in 类型ptrdiff_t定义在<cstddef>头文件中 typedef T* pointer; typedef T & reference; typedef random_access_iterator_tag iterator_category;//关于iterator_category,下面会详细介绍 } //针对 I为 const原始指针的特化版本 template<typename T> class iterator_traits<const T *> { typedef T value_type; typedef ptrdiff_t difference_type;//build-in 类型ptrdiff_t定义在<cstddef>头文件中 typedef const T* pointer; typedef const T & reference; typedef random_access_iterator_tag iterator_category;//关于iterator_category,下面会详细介绍 }
迭代器的类型 iterator_category
迭代器被分为五类:Input iterator :只读(read only)
Output iterator:只写(write only)
Forward Iterator:读写(read and write)
Bidirectional Iterator:双向迭代器,可前进可后退。
Random Access Iterator:可随机访问,跳跃式,p+n,p-n,p
,p1-p2,p1<p2等都支持。
举个例子说明iterator_traits怎么处理五种类型迭代器的,同时对traits编程技巧加上印象。
实现advance(I,n)函数,两个参数I表示迭代器,n表示前进的距离。针对不同类型的迭代器,调用效率最高的函数版本。
思路一:
定义多个版本,在对外接口中判断调用哪个版本。
//针对input iterator template<typename InputIterator,typename Distance> void _addvance_II(InputIterator & i,Distance n) { while(n--)++i; } //针对 bidirectional iterator template<typename Bidirectional,typename Distance> void _addvance_IB(Bidirectional & b,Distance n) { if(n>=0) while(n--)++b; else while(n++)--b; } //针对Random Access Iterator template<typename RandomAccessIterator,typename Distance> void _addvance_RAI(RandomAccessIterator & r,Distance n) { r+=n; } //对外接口 template<typename InputIterator,typename Distance> void advance(InputIterator i,Distance n) { //在这里判断i到底是哪种类型的迭代器 if(is_input_iterator(i)) _addvance_II(i,n); else if(is_bidirectional_iterator(i)) _addvance_IB(i,n); else 4000 _addvance_RAI(i,n); }
这种方法有个严重的缺陷:在运行期间才确定执行哪个版本,影响效率,最后在编译期间就能决定调用哪个函数。函数重载可解决这个问题,1.为了实现重载机制需要提供额外的参数来区分不同函数;2.然后再在对外接口内确定该额外参数的类型。
//五个作为标记的类型 struct input_iterator_tag{}; struct output_iterator_tag{}; struct forward_iterator_tag:public input_iterator_tag{}; struct bidirectional_iterator:public forward_iterator_tag{}; struct random_access_iterator_tag:public bidirectional_iterator{}; //针对input iterator template<typename InputIterator,typename Distance> void _addvance(InputIterator & i,Distance n,input_iterator_tag) { while(n--)++i; } //forward iterator版本不用定义,因为它的实现与 input iterator版本完全一样 //上面的五个类型标记的继承关系使得 当传进来的迭代器类型是forward iterator时 //由于没有定义该版本的_advance,所以会调用基类input iterator版本的_advance. //针对 bidirectional iterator template<typename Bidirectional,typename Distance> void _addvance(Bidirectional & b,Distance n,bidirectional_iterator) { if(n>=0) while(n--)++b; else while(n++)--b; } //针对Random Access Iterator template<typename RandomAccessIterator,typename Distance> void _addvance(RandomAccessIterator & r,Distance n,random_access_iterator_tag) { r+=n; } //对外接口 template<typename InputIterator,typename Distance> void advance(InputIterator i,Distance n) { //这里利用iterator_traits判断调用哪个版本的_advance _addvance(i,n,iterator_traits<InputIterator>::iterator_category()); }
为了规范,任何迭代器都需提供上述五个内嵌类型,以利用traits提取特性。为了方便,STL提供了一个基类 iterator,每个特定的迭代器只需继承该基类即可。
template<typename Category,
typename T,
typename Distance=pdfdiff_t,
typename Pointer= T*,
typename Reference=T&;>
class iterator
{
typedef Category iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
}
//例如 list的迭代器,类型为forward iterator
template<typename Item>
class struct ListIter:public std::iterator<std::forward_iterator_tag,Item>
{
//......
}
相关文章推荐
- STL中迭代器与traits编程的技巧
- STL源码学习——迭代器(iterators)与traits编程技法
- 《STL源码剖析》——迭代器(iterators)概念与traits编程技法(二)
- 《STL源码剖析》——迭代器(iterators)概念与traits编程技法(一)
- STL(6)之Traits编程技法 与迭代器
- STL中的traits编程技巧
- 迭代器与traits编程技法
- 【读书笔记】迭代器概念与traits编程技法
- 迭代器概念与traits编程技法
- STL中迭代器概念与traits编程技法
- 二 迭代器(四)迭代器相应型别及traits编程技法
- 【STL源码剖析读书笔记】【第3章】迭代器概念与traits编程技法
- STL源码剖析之迭代器概念与traits编程技巧--学习笔记
- STL源码剖析—迭代器与traits编程方法
- Traits 编程技法+模板偏特化+template参数推导+内嵌型别编程技巧
- 三 迭代器(iterator)概念与traits编程技法
- Traits 编程技法+模板偏特化+template参数推导+内嵌型别编程技巧
- 《STL源码剖析》读书笔记------第3 章 迭代器概念与traits编程技法
- 3 迭代器与概念和traits编程技法
- 第三章 迭代器与trait编程技巧