最后一个迭代器的相应类型就是iterator_category,就是迭代器本身的类型,根据移动特性与实行的操作,迭代器被分为了五类:

  • Input Iterator:这种迭代器所指的对象,不允许外界改变。只读(read only)。
  • Output Iterator:唯写(write only)
  • Forward Iterator:允许写入型算法在此种迭代器所形成的区间上进行读写操作,具有Input迭代器的全部功能和Output迭代器的大部分功能。支持++运算符。
  • Bidirectional Iterator:可双向移动。某些算法需要逆向遍历某个迭代器区间,可以使用Bidirectional Iterator。在Forward的基础上支持--运算符。
  • Random Access Iterator:前四种迭代器都只供应一部分指针算术能力(operator++、operator--),而这种则涵盖所有指针算数能力,包括p+n、p-n、p[n]、p1-p2、p1<p2

  这些迭代器的分类与从属关系,下图可以表示,直线与箭头代表的并非C++的继承关系,而是concept(概念)与refinement(强化)的关系。

  设计算符时,如果可能,我们尽量针对图中的某种迭代器提供一个明确的定义,并针对更强化的某种迭代器提供另一种定义,这样才能在不同情况下提供最大效率。假设有个算法可接受Forward Iterator,你以Random Access Iterator喂给它,它当然也接受,因为一个Random Access Iterator一定是一个Forward Iteartor。但是可用并不是代表最佳。

  例如最近我刚好看到copy()函数,它就为不同的迭代器定义了不同的复制方法,它提供了Input Iterator的特化版本和针对更强化的Random Access Iteartor的特化版本,为什么如此?因为于非随机迭代器而言,它们无法做到迭代器之间的减法处理(operator-()),所以每次循环判断是否达到被复制区间的尾部时,只能与last(区间为[first, last) )迭代器进行比较,这个就是Input Iterator的特化版本;而针对更强化的随机迭代器而言,因为其提供的减法处理,所以可以利用difference type记录区间大小(last - first),然后在复制循环中只用判断循环次数是否超过区间大小就好了,这个就是Random Access Iterator的特化版本,相对而言后者的速度显然比前者快很多,这就是在不同情况下提供最大效率。

  但重点是,我们必须有能力去萃取出迭代器的种类,才能调用到相应的特化版本。这最后一个迭代器的相应类型一定必须是一个类(class type),不能只是数值号码之类的东西,因为编译器需要仰赖它来进行重载判断。下面定义五个classes,代表五种迭代器类型:

 struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag :public input_iterator {};
struct bidirectional_iterator_tag :public forward_iterator_tag {};
struct random_access_iterator_tag :public bidirectional_iterator_tag {};

  这些classes只作为标记用,所以不需要任何成员。至于为什么运用继承机制,它可以促成重载机制的成功运作,另一个好处是,通过继承,我们可以不必写“单纯只做传递调用”的函数,我们可以做一个小测试:

 struct B {};            //B 可比拟为Input Iterator
struct D1 :public B {}; //D1可比拟为Forward Iterator
struct D2 :public D1 {};//D2可比拟为Bidirectional Iterator template<typename I>
func(I& p, B)
{
cout << "B version" << endl;
}
template<typename I>
func(I& p, D2)
{
cout << "D2 version" << endl;
}
int main()
{
int *p;
func(p, B()); //输出“B version”
func(p, D1()); //输出“B version”
func(p, D2()); //输出“D2 version”
}

  何为“单纯只做传递调用”的函数,如果Forward Iteartor的特化版本的功能Input Iterator也能做到,那在非继承情况下,它是如下的,以advance函数的内部函数__advance()为例,该函数具有跳到容器内某指定位置的功能:

 //Distance即为两迭代器距离类型(difference type)
template<typename InputIterator, typename Distance>
inline void __advance(InputIterator& i, Distance n, input_iterator_tag)
{
//单向,逐一前进
while (n--)++i;
} template<typename InputIterator, typename Distance>
inline void __advance(InputIterator& i, Distance n, forward_iterator_tag)
{
__advance(i, n, input_iterator_tag()); //单纯的转调用
}

  如果是继承的情况下,只需写一个Input Iteartor版本即可,而无需再写一个Forward Iteartor特化版本然后做传递调用处理。而因处理方式不一,它还提供了Bidirectional Iteartor的特化版本和Random Access Iteartor的特化版本,这里不再赘述,但有必要提一下其上层对外开放接口advance函数,这一上层接口只需两个参数,当它准备将工作转给上述的__advance()时,才自行加上第三参数:迭代器类型。因此,这个上层函数必须有能力从它所获得的迭代器推导出其类型——这份工作自然是交给traits机制:

 template<typename InputIterator, typename Distance>
inline void advance(InputIterator& i, Distance n)
{
__advance(i, n,
iterator_traits<InputIterator>::iteartor_category());
}

  iterator_traits<InputIterator>::iteartor_category()将产生一个临时对象,其类型应该隶属于前述四个迭代器类型(I、F、B、R)之一。然后,根据这个类型,编译器才决定调用哪一个__advance()重载函数。

  因此,为了满足上述行为,traits必须在增加一个相应类型:

 template<typename I>
struct iterator_traits
{
...
typedef typename I::iterator_category iterator_category;
};
template<typename T>
struct iterator_traits<T*>
{
...
//原生指针是一种Random Access Iterator
typedef typename random_access_iterator_tag iterator_category;
};
struct iterator_traits<const T*>
{
...
//原生pointer-to-const是一种Random Access Iterator
typedef typename random_access_iterator_tag iterator_category;
};

  一个迭代器的类型,应该是各种迭代器类型中最强的那个,例如int*,既是Random Access Iterator,又是Bidirectional Iterator,同时也是Forward Iterator,而且也是Input Iteartor,那么,其类型应该归属为randm_access_iterator_tag。

  另外还需注意的是STL算法的一个命名规则,如advance(),其迭代器类型参数命名为InputIteartor,这表示只要其迭代器类型的基类为input_iteartor_tag,该函数就能接受,即接受各种类型的迭代器。以算法所能接受之最低阶迭代器类型,来为其迭代器类型参数命名。

std::iterator的保证

  为了符合规范,任何迭代器都应该提供相应类型,否则便是有别于整个STL整个架构,可能无法与其他STL组件顺利搭配。因此STL提供了一个iteartor 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;
};

  iteartor class不含任何成员,纯粹只是类型定义,所以继承它并不会带来任何额外的负担。由于后三个参数皆有默认值,故新的迭代器只需提供前两个参数即可。如我们第一节土法炼钢的ListIter,如果改用正式规格,应该这么写:

 template<typename Item>
struct ListIter: public std::iterator<std::forward_iterator_tag, Item>
{...}

  设计适当的相应类型,是迭代器的责任。设计适当的迭代器,则是容器的责任。唯容器本身,才知道设计出怎样的迭代器来遍历自己,并执行迭代器该有的各种行为。至于算法,完全可以独立于容器和迭代器之外自行发展,只要设计时以迭代器为对外接口就行。

STL源码剖析——iterators与trait编程#3 iterator_category的更多相关文章

  1. STL源码剖析——iterators与trait编程#2 Traits编程技法

    在算法中运用迭代器时,很可能用到其相应类型.什么是相应类型?迭代器所指对象的类型便是其中一个.我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器 ...

  2. STL源码剖析——iterators与trait编程#4 iterator源码

    在前两节介绍了迭代器的五个相应类型,并讲述如何利用traits机制提取迭代器的类型,但始终是把iteartor_traits类分割开来讨论,这影响我们的理解,本节将给出iteator的部分源码,里面涵 ...

  3. STL源码剖析——iterators与trait编程#1 尝试设计一个迭代器

    STL的中心思想在于:将数据容器与算法分开,独立设计,再用一帖粘着剂将它们撮合在一起.而扮演粘着剂这个角色的就是迭代器.容器和算法泛型化,从技术角度来看并不困难,C++的模板类和模板函数可分别达成目标 ...

  4. STL源码剖析——Iterators与Traits编程#5 __type_traits

    上节给出了iterator_traits以及用到traits机制的部分函数的完整代码,可以看到traits机制能够提取迭代器的特性从而调用不同的函数,实现效率的最大化.显然这么好的机制不应该仅局限于在 ...

  5. 《STL源码剖析》学习之traits编程

    侯捷老师在<STL源码剖析>中说到:了解traits编程技术,就像获得“芝麻开门”的口诀一样,从此得以一窥STL源码的奥秘.如此一说,其重要性就不言而喻了.      之前已经介绍过迭代器 ...

  6. 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...

  7. STL源码剖析 迭代器(iterator)概念与编程技法(三)

    1 STL迭代器原理 1.1  迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...

  8. STL源码剖析之序列式容器

    最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...

  9. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

随机推荐

  1. python 日期

    python datetime库使用和时间加减计算  来自:https://www.cnblogs.com/linkenpark/p/8079337.html datetime库使用 一.操作当前时间 ...

  2. JMETER + POST + anti-forgery token

    JMETER + POST + anti-forgery token Looking into XSRF/CSRF Prevention in ASP.NET MVC and Web Pages it ...

  3. [转] FileZilla Server超详细配置

    FileZilla Server下载安装完成后,必须启动软件进行设置,由于此软件是英文,本来就是一款陌生的软件,再加上英文(注:本站提供中文版本,请点击下载),配置难度可想而知,站长从网上找到一篇非常 ...

  4. $objPHPExcel=$objReader->load() 报错路径不存在

    PHPexcel导入excel内容到数据库出错, $objPHPExcel=$objReader->load()报错 Could not open /public/upload/20191028 ...

  5. arcpy arcgis python实例教程--原点夹角距离定义线(坐标正算)

    arcpy arcgis python实例教程--原点夹角距离定义线(坐标正算) 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.com 此地理处理工具 ...

  6. Apache-dbutils 简介及事务处理

    一:commons-dbutils简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化 ...

  7. ISO/IEC 9899:2011 条款6.8.2——标签语句

    6.8.2 复合语句 语法 1.compound-statement: {    block-item-listopt    } block-item-list: block-item block-i ...

  8. 如何:创建返回 UI 的外接程序

    https://msdn.microsoft.com/zh-cn/library/bb909849(v=vs.100).aspx

  9. 0.9.0.RELEASE版本的spring cloud alibaba sentinel实例

    sentinel即哨兵,相比hystrix断路器而言,它的功能更丰富.hystrix仅支持熔断,当服务消费方调用提供方发现异常后,进入熔断:sentinel不仅支持异常熔断,也支持响应超时熔断,另外还 ...

  10. Linux记录-Nginx+Tomcat负载均衡配置

    Nginx负载均衡配置及策略: 轮询(默认) 优点:实现简单缺点:不考虑每台服务器的处理能力配置示例如下:upstream www.xxx.com {# 需要负载的server列表server www ...