Traits编程技法
- Traits技法就是提取相应型别,什么是相应型别?迭代器所指对象的型别便是其中之一。(除此之外还有4中所必要的型别,后面详细介绍)
- 在算法中我们要用到迭代器,假如一个算法需要使用迭代器中所指对象的型别来创建一个对象。就需要获取迭代器中的型别信息。我们知道每个容器都有专属的迭代器,所以每个容器的迭代器都含有我们所需要的相应的型别信息,所以需要一个特殊的装置帮我们提取相关的信息,那就是Traits(萃取机)
-
下面我们具体介绍一下这5个型别的具体含义
-
value_type :指迭代器所指对象的型别
-
difference_type:用来表示两个迭代器之间的距离。因此它用来表示一个容器的最大容量
typename iterator_traits<I>::difference_type; //任何时候我们需要迭代器I的difference_type 都可以这样写
-
reference_type、pointer_type :在c++函数中如果要传回左值(允许改变所指对象的内容),都是以by reference 的方式进行;就pointer_type而言,我们能够传回一个pointer,指向迭代器所指之物。
Item& operator*()const{return *ptr;} Item* operator->()const{return ptr;} //Item&就是reference_type Iter*就是pointer_type
-
iterator_category:简单来说就是提取迭代器类型,
为算法选择最有利于其功能的迭代器类型
(迭代器类型有单向,双向,只读,只写,随机 迭代器)。- traits有能力提取迭代器的种类,我们利用其作为算法的一个参数,这个类别必须是class type,然后就能调用算法的不同形式,以提升效率
//下面我将以advance()算法来描述这个类别 //首先是5个用作标记的型别 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(); //下面是advance()算法的具体实现 template<class InuputIterator,class Distance> inline void __advance(InputIterator& i,Distance n,input_iteratoo_tag){ ... } template<class ForwardIterator,class Distance> inline void __advance( ForwardIterator& i,Distance n, forward_iterator_tag){ ... } template<class BidirectionalIterator,class Distance> inline void __advance( BidirectionalIterator& i,Distance n, bidirectional_iterator_tag){ ... } template<class RandomAccesslIterator,class Distance> inline void __advance( RandomAccesslIterator& i,Distance n, random_access_iterator_tag){ ... }//以上函数的具体实现不同 //每个__advance都只声明类别,不指定参数,只是纯粹用来激活重载机制的。
这时我们需要准备一个上层接口,它只接受两个参数,当它把上述工作给__advance()时,才自行加上第三参数:迭代器类型。然后这个函数必须从迭代器中推导出类型。当然这个工作交traits。
template<class InputIterator,class Distance> inline void advance(InputIterator &i,Distance n){ __anvance(i,n,iterator_traits<InpurIterator>::iterator_category()); }
为了满足上述行为,traits必须还要增加一个相应的型别:iterator_category;
template<class I> struct iterator_traits{ ... typedef typename I::iterator_category iterator_category; }; //另外还有两个偏特化版本,是针对原始指针设计而成,T*,const T*.他们的迭代器类型都是随机访问迭代器。
-
注意:任何一个迭代器都应该落在"该迭代器隶属中"最强化的那一个。例如一个迭代器int*是上面的那5个迭代器类型,但是在程序中我们必须把它当作随机访问迭代器;
STL算法的命名法则:以算法所能接受的最低阶迭代器类型,来为其迭代器类型命名。
-
在上面我所列举的5个类型的型别定义中,后面3个为什么要公有继承前面的两个迭代器类型,为了消除“单纯传递调用的函数”,比如当客端调用distance()并使用Forward iterator时,统统都会调用Input iterator版的那个的__distance()。因为distance()算法里没有Forward iterator 版本。如果没有继承,我们就会在distance()的Forward版本里调用Input版本的。
-
-
iterator的保证
-
为了规范,每个迭代器都要提供这5个类型。为了以防你初心,STL提供了一个iterator class 每个迭代器都可以继承它,以保证规范
template<class Category,class T,class Distance=ptrdoff_t,class Pointer=T*,class Refrence=T&> struct iterator{ typedef Category iterator_category; typedef T value_type; typedef Distance difference_type; typedef Pointer pointer; typedef Reference reference; }; //由于后面3个参数是默认的,所以我们在继承的时候必须指定前面的2个参数
-
总结:设计适当的型别是迭代器的责任,设计合适的迭代器是容器的责任,只有容器才知道设计出怎样的迭代器来遍历自己,并执行迭代器该执行的各种行为。至于算法完全独立于容器于迭代器,自行发展,只是设计时以迭代器为对外接口形式。