版权声明:本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名elloop(包含链接)
本系列文章的目录在这里:目录. 通过目录里可以对STL总体有个大概了解
#前言
本文介绍了STL中的迭代器相关的类型和特性,它们用来定义和区分不同的迭代器类型。如iterator tag作为迭代器的“标签”用来区分迭代器的类型;iterator traits定义了所有类型的迭代器都应该有的公共信息。那标准库为什么提供这些东西呢?答案是我们可以根据这些信息来编写泛型代码,在泛型代码里根据iterator traits来判定迭代器的类型以做相应处理、可以自己定义迭代器实现自定义的迭代器操作。
iterator tag
下面的代码描述了五中类型的迭代器的“标签”,其中有几种类型的继承关系, 这包含了面向对象的“IS A”的含义。例如,从forward_iterator_tag
和bidirectional_terator_tag
的继承关系可知,它们分别对应的迭代器类型,Bidirectional Iterator “IS A” Forward Iterator. 意味着可以用Forward Iterator 的地方,丢一个Bidirectional Iterator过去也是可以的。
iterator traits
iterator traits :迭代器特性,定义了所有迭代器都有的公共类型信息:
有了iterator_traits
, 我就可以这样, 定义一个迭代器(类型为T)所指向的值类型的变量:
std::iterator_traits<T>::value_type val;
使用iterator traits来编写泛型代码
使用迭代器定义的数据类型, 如 value_type
在算法内部使用迭代器定义的数据类型value_type
使用迭代器分类 iterator_category
分两步:
-
在定义的模板函数f内部,调用另一个以iterator_category
作为额外参数的函数f, 完成差异化处理;
-
实现第一步中重载的f,使用不同类型的iterator_category
参数以针对特殊类型迭代器做特殊处理。
上面两个特殊的重载版本的f分别针对随机迭代器和双向迭代器做特殊处理。由于前面介绍的iterator tag的继承关系,可以只针对某个父类定义一个f,该父类的所有子类都可以共用同一个f。比如下面的例子:
distance() 函数的实现:
从distance()的实现可以看出,随机迭代器将使用迭代器的算术运算pos2-pos1搞定问题;第二个版本的distance()则能够同时针对Input Iterator, Forward Iterator和Bidirectional Iterator三类迭代器起作用,这正是由开篇给出的iterator tag的继承关系决定的。
自定义迭代器
从前面的介绍可知,要自己写一个迭代器, 需要提供iterator_traits
里的五个类型。可以通过两种办法来实现这一需求:
-
在我的迭代器类型内,定义iterator_traits
里需要的五个东西。
-
特化或者偏特化模板iterator_traits
, 就像iterator_traits
的数组特化版本那样。
下面使用《c++标准库》里面的一个例子,来说明使用第一种方法来实现自定义迭代器。
C++标准提供了一个特殊的基类:iterator<>
, 它帮我们完成了五个类型的定义,我们只需要继承这个基类,并指定它需要的类型参数即可, 例如如下定义:
第一个参数指定了迭代器的分类:forward_iterator_tag
, bidirectional_iterator_tag
或者 random_access_iterator_tag
等等
第二个参数指定了元素类型:value_type
第三个参数指定了位置差类型:difference_type
第四个参数指定了指针类型: pointer
, 第五个参数指定了引用类型:reference
最后的三个参数的默认值分别是:ptrdiff_t
, type*
, type&
, 在使用时可以忽略。
下面的例子就是书中的例子,它定义了一个关联容器的inserter适配器,类似std::inserter, 相对于std::iterator, 它省去了位置参数,传入一个容器即可:
用法:
这里的用法示例与其他的迭代器适配器的用法类似,请参考【C++ STL应用与实现】18: 如何使用迭代器适配器.
源码及参考链接
在这里也能看到这篇文章:github博客, CSDN博客, 欢迎访问