STL的迭代器和类型萃取
今天就可以把STL库中迭代器的实现,和类型萃取好好整理一下了
迭代器的设计思维是STL的关键所在,在STL的实际运用和泛型思维,迭代器都扮演着十分重要的角色,STL力求把数据容器和算法的概念分开来,于是就有了STL的两大部分,容器(container)和泛型算法(algorithms),泛型算法有很多参数都是迭代器。
举一个栗子!泛型算法find()的实现!
template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last , const T& value)
{
while(*first != *last && *first != value)
{
++first;
}
return first;
}
接受两个输入迭代器的参数
- STL把迭代器分为5个类别
- trivial迭代器(Trivial Iterator):只是一个概念,用以区分所有迭代器的定义
- 输入迭代器(Input iterator):提供只读操作,可读取其指向元素的值,但不可以改变该元素的值(还包括trivial迭代器中的所有操作(可能是用继承?))
- 输出迭代器(Output Iterator):提供只写操作,可改变
- 前进迭代器(Forward Iterator):可以改变所指向元素的值,但不可以读取
- 双向迭代器(Bidirectional Iterator):可以双向移动
- 随机访问迭代器(Random Iterator):韩乾各种指针算数能力,可以像访问顺序表一样随机访问
之前发布的智能指针也算是迭代器的一种,原生的指针当然也可以说是迭代器,只是这么说有些狭隘了迭代器的定义,因为自定义类型的指针功能不够完善,需要重载一些运算符之类的,为什么智能指针也不能直接用作迭代器呢?因为无法获得智能指针所指向的类型!
为什么这么说?因为有的时候我们可能想要使用智能指针所指向的类型去创建一些变量,不好弄,即使是使用type_id也只是获得名字,并不能用来创建变量,有种方法可以用的
template<class T,class U>
void Fun_impl(T t ,U u)
{
U tmp;//we got the type
}
template<class T>
void Fun(T t)
{
return Fun_impl(t ,*t);
}
这么做就可以获得所指向的类型(并且创建了想要的变量)了,但是当我们需要一个这样的返回值的时候应该怎么办嘛,也许还是上面这种方法?看似是那么的完美,但是编译之后就无情的击碎了你的希望,编译是不通过的
但是我们可以通过内嵌型别声明来获取他们的类型(通过typeid是不行的,因为typeid只能获得类型的名称,并不能用这个名称来声明变量)
这种方法显然是可以的,但是有个隐晦的陷阱,因为不是所有传过来的都是迭代器,万一传过来的就是一个原生指针,原生指针就没有内嵌类型(当然没有,你认为一个int*内部会有内嵌类型么,话说int*有什么内部么)!!!!这就要用到偏特化的知识
那什么是偏特化呢?使用模板的时候,模版参数设定为多个template<class T, class U>模板是可以特定的
template<class T, class U>
class class1
{
T a;
U b;
};
template<class T>
class class1<int>
{
T a;
U b;
};
上述代码中对U进行了特化,嗯!简单易懂,如果T也特化一下,就是全特化了,偏特化就是没有全都特化!

但是还有一个问题,当传过来的的指针是一种指向常值的指针(pointers to const)const int *ptr的时候,,我们很显然获得的类型就是const int,这有些不对劲,因为我们得到的类型拿来声明临时变量是没有办法使用的,就因为他指向了常值,很简单,再拿取偏特化一下就可以了(就是在上图中<T*>处改成<const T*>)
traits就扮演了一个类型萃取机的角色,用来萃取出迭代器所指向的类型(类型萃取?!)

为什么要有这么多的迭代器呢?你看容器人手一个!因为迭代器中有operator++的实现,每个容器存储的方式也不尽相同,vector是顺序存储,list是链式存储,operator++内部实现肯定不一样的,但是又想泛型编程,继承咯,封装咯。。。
- 最常使用到的迭代器的相应类别有5种:value_type,difference_type,pointer,reference,iterator_category
- value_type就是迭代器所指向的对象的型别,每个迭代器都应该定义有自己的value_type内嵌型别
- difference_type用来表示两个迭代器之间的距离,因此也可以表示一个容器的最大容量
- referecne引用?左值,因为有需要const iterator的情况出现,这时候需要一个不能改变所指向对象的迭代器
;在C++中,函数如果要传回左值,都是以by reference的方式进行 - pointer_type就是简单的迭代器所指向对象的地址,就是图中第二个

- iterator_category就是之前用的那个类型萃取,萃取出迭代器的名称,作为函数参数,激活函数重载机制,就是利用traits实现的
那么我接下来就讲讲iterator_category的相关吧
iterator_category干啥的?对下图中五种迭代器进行分类标识
这张图中的箭头并不是代表继承关系,而是概念强化关系,就是说下层一定是个上层,但上层绝不是个下层,就跟正方形是矩形的一种特殊一样,但是矩形就不是正方形
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(iterator ,n)泛型函数,它的作用是将传过来的迭代器往后走n个,每种迭代器的实现肯定不一样的,像是forward_iterator只能一个一个走,走n次,bidirectional_iterator也一样,random_access_iterator就可以一下走n个,如果写成一个,肯定会影响random_access_iterator的效率,所以分开写,那么我怎么知道改调用哪个函数呢?因为我想泛型编程(就是我懒,我想省事)肯定就想只写一个函数名,传个参数,让程序帮我选择调用哪个函数(我的天!这不就是函数重载么),但是程序需要知道我传的参数是哪种类型的迭代器(就是类型萃取呢!!!)把类型从迭代器中萃取出来!
(没弄全,其他的类似)
参数列表中最后一个就是刚才建立的那五个空壳结构体!!调用_advance的时候最后一个参数给迭代器的iterator_category就行了,对!没错就这么用!让程序自己判断用哪个函数!!
具体实现:调用
template <class InputIterator ,class Distance>
inline void advance(InputIterator& i,Distance n)
{
_advance(i, n, iterator_traits<InputIterator>::iterator_category());
}
template<class I>
struct iterator_traits
{
......
typedef typename I::iterator_category iterator_category;
};
STL的迭代器和类型萃取的更多相关文章
- C++的类型萃取技术
应该说,迭代器就是一种智能指针,因此,它也就拥有了一般指针的所有特点——能够对其进行*和->操作.但是在遍历容器的时候,不可避免的要对遍历的容器内部有所了解,所以,设计一个迭代器也就自然而然的变 ...
- 类型萃取(type traits)
1. 类型萃取的作用 类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特性,用以判断该类型是否含有某些特性,从而在泛型算法中来对该类型进行特殊的处理用来提高效率或者其他.例如:在STL ...
- C++类型萃取
stl中的迭代器和C++中的类型萃取: http://www.itnose.net/detail/6487058.html 赐教!
- 【C++】模板简述(五):类型萃取
功能 类型萃取,在STL中用到的比较多,用于判断一个变量是否为POD类型. 简述来说可以用来判断出某个变量是内置类型还是自定义类型. 通过类型萃取,萃取到变量类型,对不同变量进行不同处理,可以提升程序 ...
- 头一回发博客,来分享个有关C++类型萃取的编写技巧
废话不多说,上来贴代码最实在,哈哈! 以下代码量有点多,不过这都是在下一手一手敲出来的,小巧好用,把以下代码复制出来,放到相应的hpp文件即可,VS,GCC下均能编译通过 #include<io ...
- 第17课 类型萃取(1)_基本的type_traits
1. type_traits类型萃取 (1)type_traits通过定义一些结构体或类,并利用模板类特化和偏特化的能力,给类型赋予一些特性,这些特性根据类型的不同而异.在程序设计中可以使用这些tra ...
- c++11——type_traits 类型萃取
一. c++ traits traits是c++模板编程中使用的一种技术,主要功能: 把功能相同而参数不同的函数抽象出来,通过traits将不同的参数的相同属性提取出来,在函数中利用这些用tr ...
- 侯捷C++ Type traits(类型萃取
泛型編程編出來的代碼,適用於任何「吻合某種條件限制」的資料型別.這已成為撰寫可復用代碼時的一個重要選擇.然而,總有一些時候,泛型不夠好 — 有時候是因為不同的型別差距過大,難以產生一致的泛化實作版本. ...
- 第19课 类型萃取(3)_类型选择的traits
1. std::conditional (1)原型:template <bool Cond, class T, class F> struct conditional; //根据条件获取T ...
随机推荐
- Scrum7.0
Sprint回顾 让我们一次比一次做得更好. 1.回顾组织 主题:“我们怎样才能在下个sprint中做的更好?” 时间:1个小时 参与者:整个团队 场所:课室 秘书:陈程 2.回顾流程 (1)spri ...
- mobx源码解读4
这节介绍一下mobx的变动因子的稳定性. mobx整个系统是由ObservableValue, ComputedValue, Reaction这三个东西构建的 ObservableValue 是最小的 ...
- mobx源码解读2
我们将上节用到的几个类的构造器列举一下吧: function Reaction(name, onInvalidate) { if (name === void 0) { name = "Re ...
- Go Data Structures: Interfaces
refer:http://research.swtch.com/interfaces Go Data Structures: Interfaces Posted on Tuesday, Decembe ...
- web前端相关网站
传智播客. (有很多公开课的,而且他的百度云盘也有很多东西) http://www.itcast.cn/(这个不是广告,爱信不信) 慕课网 ...
- Android 二维码 生成和识别(附Demo源码)
今天讲一下目前移动领域很常用的技术——二维码.现在大街小巷.各大网站都有二维码的踪迹,不管是IOS. Android.WP都有相关支持的软件.之前我就想了解二维码是如何工作,最近因为工作需要使用相关技 ...
- easyui datagrid 点击列表头排序出现错乱的原因
之前我的导师,也就是带我的同事,使用datagrid,发现点击列表头排序出现乱序,按理说只有顺序和逆序两种排序结果.因为他比较忙,当时没解决,把排序禁掉了,后来又要求一定要排序,所以他交给我. 一开始 ...
- MLA Handbook for Writers of Research Papers笔记
MLA Handbook for Writers of Research Papers.7th ed.New York:MLA,2009.print.还有一本,留待阅读MLA Style Manual ...
- git 创建远程分支和删除 master 分支
. . . . . 最近需要将不同的客户的代码分开管理,所以需要为这些代码分别创建分支. 目前版本库中分支结构如下: [yuhuashi@local:Project]$ git branch -a* ...
- js 阻止浏览器默认行为
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...