STL源码剖析 5中迭代器型别

  •  最常使用的5种迭代器的型别 为 value_type、difference_type、pointer、reference、iterator_category。
  • 如果想要自己开发的容器和STL进行适配,就需要定义上述5种类型 
  • iteraor_traits 必须针对传入的型别为 pointer 或者 pointer-to-const设计偏特化版本
template <class I>
struct iteraor_traits{typedef typename I::iterator_category iterator_category;typedef typename I::value_type value_type;typedef typename I::difference_type difference_type;typedef typename I::pointer pointer;typedef typename I::reference reference;
};

value_type

  • 迭代器所指对象的型别 

difference_type

  • 两个迭代器之间的距离,表示容器的最大容量([begin,end))
  • 例:STL 的count() 返回的数值就是迭代器的 difference type
template <class I>
struct iteraor_traits{typedef typename I::iterator_category iterator_category;typedef typename I::value_type value_type;typedef typename I::difference_type difference_type;typedef typename I::pointer pointer;typedef typename I::reference reference;
};template <class I,class T>
typename iteraor_traits<I>::difference_type //这一整行限定的是函数的回返型别
count (I first,I last,const T& value){typename iteraor_traits<I>::difference_type n = 0;for (;first != last;++first) {if (*first == value){++n;}}return n;
}
  • 针对相应型别differe type,traits 的如下两个(针对原生指针而写的)特化版本
  • 使用C++内建的ptrdiff_t (定义在<cstddef>头文件) 作为原生指针的difference type
//针对原生指针设计的偏特化版本
template <class T>
struct iteraor_traits<T*>{typedef ptrdiff_t difference_type;
};//针对原生的pointer-to-const设计的偏特化版本
template <class T>
struct iteraor_traits<const T*>{typedef ptrdiff_t difference_type;
};
  • 当我们需要的任何迭代器I的difference type,可以怎么写 typename iterator_traits<I>::difference_type 

pointer

  • 指针和引用之间的关系是非常密切的,传回一个左值,令他代表所指之物,也可以令其代表所指之物的地址,即可以通过传回一个指针,指向迭代器的所指之物
Item& operator*() const {return *ptr;}
Item* operator->() const {return ptr;}
//针对原生指针设计的偏特化版本
template <class T>
struct iteraor_traits<T*>{typedef T* pointer;typedef T& reference;
};//针对原生的pointer-to-const设计的偏特化版本
template <class T>
struct iteraor_traits<const T*>{typedef const T* pointer;typedef const T& reference;
};

reference

  • 从迭代器所指之物的内容是否允许改变的角度出发,迭代器分为两种:不允许改变所指对象的内容 称为const int*pic
  • 允许改变所指之物的内容 称为mutable iterators ,例如int * pi;
  • 对于mutable iterator进行提领操作的时候,得到的不是一个右值(右值不允许赋值操作),应该是一个左值。
    int *pi = new int(5);const int* pci = new int(9);*pi = 7; //对mutable iterator进行提领操作的时候 得到的是左值,允许赋值*pci = 1; //这个操作是不允许的,pci是const iterator//提领pci得到的结果是一个 右值 不允许赋值
  • C++左值是通过reference的方式返回的
  • 当p是mutable iterator时,其value type是T  那么*p型别不应该是T 应该是T&
  • 当p是constant iterator时,其value type是const T  那么*p型别不应该是const T 应该是const T&

iterator_category

//针对原生指针设计 偏特化版本
template <class T>
struct iteraor_traits<T*>{//注意 原生指针是一种Random access iteratortypedef random_access_iterator_tag iterator_category;
};//针对原生指针 pointer-to-const 设计 偏特化版本
template <class T>
struct iteraor_traits<const T*>{//注意 原生指针pointer-to-const 是一种Random access iteratortypedef random_access_iterator_tag iterator_category;
};

迭代器的分类( 根据移动特性和施行操作 )

  • Input iterator :不允许外界改变 只读
  • output iterator:唯写
  • forward iterator:允许写入型算法 (例如 replace() ) 在这种迭代器形成的区间上进行读写操作
  • bidirectional iterator : 可以双向移动 ,可以逆向访问迭代器的区间(例如逆向 拷贝某个范围内的元素)
  • random access iterator : 前四种仅仅具备一部分指针算数的能力(前三种支持operator++,第四种需要加上operator--);第五种需要涵盖所有的只针的算数的能力,比如p+n p-n  p[n]  p1-p2 p1<p2

  • 直线和箭头并不代表 继承的关系,而是所谓的 概念 和强化的关系
  • 例子 使用advance函数举例,这个函数有两个参数,迭代器p和数值n,函数需要实现迭代器p累计前进n次
  • 3个例子:input iterator、bidirectional  iterator 、random access iterator
  • 倒是没有针对forwarditerator设计的版本,因为这个 和 inputiterator而设计的版本完全一致
template <class InputIterator,class Distance>
void advance_II(InputIterator& i,Distance n){//单向 逐一前进while (n--)++i;
}template <class BidirectionalIterator,class Distance>
void advance_BI(BidirectionalIterator& i,Distance n){//双向 逐一前进if(n >= 0){while (n--){++i;}} else{while (n++){--i;}}
}template <class RandomAccessIterator,class Distance>
void advance_RAI(RandomAccessIterator&i ,Distance n){//双向 跳跃前进i += n;
}
  •  程序调用advance()的时候 具体使用哪一个函数定义呢?如果选择advance_II()对于randomaccess iterator而言 缺乏效率,O(1)操作变成了O(N)操作
  • 如果是advance_RAI() 则无法接受 Input Iterator  需要对上述三个函数进行合并
template <class InputIterator,class Distance>
void advance(InputIterator& i,Distance n){if (is_random_access_iterator(i)){advance_RAI(i,n);} else if (is_bidiractional_iterator(i)){advance_BI(i,n);} else{advance_II(i,n);}
}
  • 在执行期间决定使用哪一个版本,会影响程序的执行的效率,最好在编译期间就确定程序执行的正确的版本,因此使用函数的重载是最好的方式
struct _LIBCPP_TEMPLATE_VIS input_iterator_tag {};
struct _LIBCPP_TEMPLATE_VIS output_iterator_tag {};
struct _LIBCPP_TEMPLATE_VIS forward_iterator_tag       : public input_iterator_tag {};
struct _LIBCPP_TEMPLATE_VIS bidirectional_iterator_tag : public forward_iterator_tag {};
struct _LIBCPP_TEMPLATE_VIS random_access_iterator_tag : public bidirectional_iterator_tag {};
  • 上述这些classes只能作为标记使用,不需要任何成员
  • 加上第三个参数,让他们之间形成重载的关系
template <class InputIterator,class Distance>
void __advance(InputIterator& i,Distance n,input_iterator_tag){//单向 逐一前进while (n--)++i;
}//这是一个单纯的传递调用的函数(trivial forwarding function)
template <class ForwardIterator,class Distance>
inline void __advance(ForwardIterator&i, Distance n,forward_iterator_tag){//单纯的进行传递调用 (forwarding)advance(i,n,input_iterator_tag());
}template <class BidirectionalIterator,class Distance>
void advance_BI(BidirectionalIterator& i,Distance n,bidirectional_iterator_tag){//双向 逐一前进if(n >= 0){while (n--){++i;}} else{while (n++){--i;}}
}template <class RandomAccessIterator,class Distance>
void advance_RAI(RandomAccessIterator&i ,Distance n,random_access_iterator_tag){//双向 跳跃前进i += n;
}
  • __advance()的第三个参数只声明了型别,但是并没有指定参数的名称,其主要的目的是为了激活重载机制,函数中根本不使用这个参数
  • 还需要一个对外开放的上层控制接口,调用上述的各自重载的__advance() ,这一层只需要两个参数,当其准备将工作转移给上述的_advance()时才会自行加上第三个参数:迭代器的类型。
  • 使用traits机制 从所获得的迭代器中推导出 其类型
template <class I>
struct iteraor_traits{typedef typename I::iterator_category iterator_category;typedef typename I::value_type value_type;typedef typename I::difference_type difference_type;typedef typename I::pointer pointer;typedef typename I::reference reference;
};template <class InputIterator,class Distance>
inline void advance(InputIterator&i, Distance n){__advance(i,n,iteraor_traits<InputIterator>::iterator_category());
}
  • iteraor_traits<InputIterator>::iterator_category() 将产生一个暂时对象,就像int() 产生一个int暂时对象一样。
  • 这个临时对象的型别应该隶属于前面所述的5个迭代器之一,然后根据这个 迭代器的型别,编译器才决定调用哪一个_advance()重载函数

 注意事项

  • 迭代器其类型隶属于各个适配类型中最强化的那个。例如int* 既是random 、bidirectional、forward、input,那么其类型应该从属为random_access_iterator_tag
  • 但是迭代器的参数需要按照最低阶的类型为其命名,比如advance()函数,使用最低级的inputIterator为其命名

消除 “单纯传递调用的函数”

  • 使用class定义迭代器的各种分类标签,不仅可以促进重载机制的成功运作,使得编译器得以正确执行重载决议,overloaded resolution
  • 还可以 通过继承不比再写 "单纯只做传递调用"的函数,即advance不需要写 Forwarditerator这个版本

  • 仿真测试tag types继承关系所带来的影响

  •  例子:使用distance举例子 计算两个迭代器之间的距离

  • distance可以接受任何类型的迭代器,但是template型别参数设置为InputIterator,是为了遵循STL算法的命名规则,使用最低级的迭代器命名
  • 考虑到继承关系,当客户端调用distance()函数输入的参数型别是 Output iterators、Bidirectional Iterators、ForwardIterator时,统统都会转化为Input Iterator版_distance函数
  • 即存在继承关系的模型,仅仅实现首 和 尾即可,中间版本都会被隐式转化为首,尾巴调用专属的函数即可

 std::iterator 保证

  • 规范需要任何的迭代器都需要实现上述的五个内嵌的型别,从而方便traits进行类型的萃取,否则无法适配 STL的组件
  • STL提供了iterator class,新设计的迭代器需要继承自它,它不包含任何的成员,只是进行了型别的定义,因此继承它 不会导致额外的负担
#include <memory>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{};//自行开发的迭代器需要继承 std::iterator
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;
};//traits
template <class Iterator>
struct iterator_traits{typedef typename Iterator::iterator_category iterator_category;typedef typename Iterator::value_type value_type;typedef typename Iterator::difference_type difference_type;typedef typename Iterator::Pointer pointer;typedef typename Iterator::reference reference;
};//针对原生指针(native pointer)设计traits偏特化版本
template <class T>
struct iterator_traits<T*>{typedef random_access_iterator_tag iterator_category;typedef T                          value_type;typedef ptrdiff_t                  difference_type;typedef T*                         pointer;typedef T&                         reference;
};//针对原生指针(pointer-to-const)设计的traits偏特化版本
template <class T>
struct iterator_traits<const T*>{typedef random_access_iterator_tag iterator_category;typedef T                          value_type;typedef ptrdiff_t                  difference_type;typedef T*                         pointer;typedef T&                         reference;
};//函数的目的是为了 方便的决定某个迭代器的类型 (category)
template <class Iterator>
inline typename iterator_traits<Iterator>::iterator_category
iterator_category(const Iterator&){typedef typename iterator_traits<Iterator>::iterator_category category;return category();
}//函数的目的是为了 方便的决定某个迭代器的类型 distance type
template <class Iterator>
inline typename iterator_traits<Iterator>::difference_type *
distance_type(const Iterator&){return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
}//函数的目的是为了 方便的决定某个迭代器的类型 value type
template <class Iterator>
inline typename iterator_traits<Iterator>::value_type  *
value_type(const Iterator&){return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
}//整组的distance函数
template <class InputIterator>
inline typename iterator_traits<InputIterator>::difference_type
__distance(InputIterator first,InputIterator last,input_iterator_tag){typename iterator_traits<InputIterator>::difference_type n = 0;while (first != last){++first;++n;}return n;
}template <class RandomAccessIterator>
inline typename iterator_traits<RandomAccessIterator>::difference_type 
__distance(RandomAccessIterator first,RandomAccessIterator last,random_access_iterator_tag){return (last - first);
}template <class InputIterator>
inline typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first,InputIterator last){typedef typename iterator_traits<InputIterator>::iterator_category category;return (__distance(first,last,category()));
}//以下是整组的advance函数
template <class InputIterator,class Distance>
inline void __advance(InputIterator& i,Distance n,input_iterator_tag){while (n--){++i;}
}template <class BidirectionalIterator,class Distance>
inline void __advance(BidirectionalIterator&i,Distance n,bidirectional_iterator_tag){if (n>=0){while (n--) ++i;} else{while (n++) --i;}
}template <class RandomAccessIterator,class Distance>
inline void __advance(RandomAccessIterator& i,Distance n,random_access_iterator_tag){i+=n;
}template<class InputIterator,class Distance>
inline void advance(InputIterator& i,Distance n){__advance(i,n,iterator_category(i));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446288.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python学习16 正则表达式3 练习题

用户名匹配 1.用户名匹配&#xff1a;由数字、大小写字母、下划线_、中横线-组成&#xff0c;长度为6-12位&#xff0c;不能以数字开头。 import re usernameab578_-SDF resultre.search(^[a-zA-Z_-][0-9a-zA-Z_-]{5,12}$,username) print(result)邮箱 2.验证输入的邮箱&…

加载tf模型 正确率很低_深度学习模型训练全流程!

↑↑↑关注后"星标"Datawhale每日干货 & 每月组队学习&#xff0c;不错过Datawhale干货 作者&#xff1a;黄星源、奉现&#xff0c;Datawhale优秀学习者本文从构建数据验证集、模型训练、模型加载和模型调参四个部分对深度学习中模型训练的全流程进行讲解。一个成…

Python学习17 Turtle库绘图

学习网址&#xff1a;https://docs.python.org/zh-cn/3/library/turtle.html Turtle库 Turtle库是Python语言中一个很流行的绘制图像的函数库&#xff0c;一个小乌龟&#xff0c;在一个横轴为x、纵轴为y的坐标系原点&#xff08;画布中心&#xff09;&#xff0c;(0,0)位置开…

android ros 节点编写_嵌入式的我们为什么要学ROS

前言本来是要写一篇STM32移植ROS的一个小lib库&#xff0c;ROS一般都是需要跑在Linux上的&#xff0c;STM32使用就是当成一个ROS通讯的小节点&#xff0c;但是写文章时间不够&#xff0c;所以就简单做一篇ROS的介绍文章&#xff0c;分享给嵌入式的小伙伴们。ROS现在在机器人领域…

STL源码剖析 __type_traits

traits编程 弥补了C本身的不足STL只对迭代器进行规范制定出了iterator_traits&#xff0c;SGI在此基础上进一步扩展&#xff0c;产生了__type_traits双下划线的含义是这个是SGI内部使用的东西&#xff0c;不属于STL标准iterator_traits 负责萃取迭代器的特性__type_traits负责萃…

java 学生成绩

题目 对学生成绩大于60分的&#xff0c;输出“合格”。低于60分的&#xff0c;输出“不合格” 代码 使用/除法简化代码 package l1_switch_case;import java.util.Scanner;public class SwitchDemo2 {public static void main(String[] args) {Scanner scanner new Scanne…

STL源码剖析 序列式容器|Vector

容器的概观和分类 array 数组 、list 链表、tree树 、stack堆栈、queue队列、hash table散列表、set集合、map映射表根据数据在容器中的排列顺序&#xff0c;将上述数据结构分为序列式和关联式两种类型SGI STL使用内缩方式来表达基层和衍生层之间的关系衍生不是派生&#xff0…

ansible 修改文件变量_Ansible Playbook中的变量与引用

Ansible是一个系列文章&#xff0c;我会尽量以通俗易懂、诙谐幽默的总结方式给大家呈现这些枯燥的知识点&#xff0c;让学习变的有趣一些。Ansible自动化运维前言前面有说到使用playbook来搞一些复杂的功能&#xff0c;我们使用YAML来写playbook&#xff0c;就像我们用其它语言…

java 判断日期为第几天

题目1 编写程序&#xff1a;从键盘上输入2019年的“month”和“day”&#xff0c;要求通过程序 输出输入的日期为2019年的第几天。 代码1 从12月往下加日期数 package l1_switch_case; import java.util.Scanner; public class SwitchDemo4 {public static void main(Strin…

STL源码剖析 list概述

目录 list的节点(node) list迭代器 list 的构造和内存管理 list 的元素操作 list相较于vector连续的线性空间就显得很复杂&#xff0c;他的存储空间是不连续的&#xff0c;好处是每次插入和删除一个元素的时候&#xff0c;只需要配置或者释放一个元素的空间 插入和删除十分的…

vsftp不允许切换到其它目录_IntelliJ IDEA如何对project的目录进行筛选显示?

如果你的项目很庞大&#xff0c;同一个功能用到的各种文件散落在多个文件夹&#xff0c;开发时切换不便&#xff0c;可以利用scope功能&#xff0c;只显示该功能用到的文件&#xff0c;让project列表十分清爽&#xff0c;提高开发效率。本文使用的IDEA版本为2020.1。1、打开sco…

java 年份对应的中国生肖

题目 编写一个程序&#xff0c;为一个给定的年份找出其对应的中国生肖。 中国的生肖基于12年一个周期&#xff0c; 每年用一个动物代表&#xff1a; rat、ox、tiger、rabbit、dragon、snake、horse、sheep、monkey、 rooster、dog、pig。 提示&#xff1a;2019年&#xff1a;猪…

密码学专题 对称加密算法

一般来说&#xff0c;使用OpenSSL对称加密算法有两种方式&#xff0c;一种是使用API函数的方式&#xff0c;一种是使用OpenSSL提供的对称加密算法指令方式。本书将介绍对称加密算法的指令方式OpenSSL的对称加密算法指令主要用来对数据进行加密和解密处理&#xff0c;输入输出的…

网络防火墙单向和双向_单向晶闸管与双向晶闸管之间的不同之处

晶闸管是回一个可以控导点开关&#xff0c;能以弱电去控制强电的各种电路。晶闸管常用于整流&#xff0c;调压&#xff0c;交直流变化&#xff0c;开关&#xff0c;调光等控制电路中。具有提交小&#xff0c;重量轻&#xff0c;耐压高&#xff0c;容量大&#xff0c;效率高&…

java 遍历100以内的偶数,偶数的和,偶数的个数

题目 遍历100以内的偶数&#xff0c;偶数的和&#xff0c;偶数的个数 代码 package l2_for; /*遍历100以内的偶数&#xff0c;偶数的和&#xff0c;偶数的个数*/ public class ForDemo1 {public static void main(String[] args) {//方法1&#xff1a;int sum1 0,count10;f…

python版本切换_怎么切换python版本

展开全部 &#xff08;1&#xff09;分别安2113装 python-2.7.12.amd64.msi python-3.5.2-amd64.exe &#xff08;python官网下载的&#xff09; 顺序无所谓&#xff08;为5261了看着4102方便&#xff0c;我把安装路径修改统一了1653&#xff09; &#xff08;2&#xff09;配置…

java 打印

题目 编写程序从1循环到150&#xff0c;并在每行打印一个值&#xff0c;另外在每个3的倍数行 上打印出“foo”,在每个5的倍数行上打印“biz”,在每个7的倍数行上打印 输出“baz”。 代码 package l2_for;/** 编写程序从1循环到150&#xff0c;并在每行打印一个值&#xff0c…

react.lazy 路由懒加载_Vue面试题: 如何实现路由懒加载?

非懒加载import List from /components/list.vue const router new VueRouter({routes: [{ path: /list, component: List }] })方案一(常用)const List () > import(/components/list.vue) const router new VueRouter({routes: [{ path: /list, component: List }] })方…

STL源码剖析 deque双端队列 概述

vector是单向开口的连续线性空间&#xff0c;deque是一种双向开口的连续线性空间。deque可以在头尾两端分别进行元素的插入和删除操作vector和deque的差异 1&#xff0c;deque允许常数时间内对于头端元素进行插入和删除操作2&#xff0c;deque没有所谓容量(capacity)的概念&…

java 最大公约数和最小公倍数

题目 题目&#xff1a;输入两个正整数m和n&#xff0c;求其最大公约数和最小公倍数。 比如&#xff1a;12和20的最大公约数是4&#xff0c;最小公倍数是60。 说明&#xff1a;break关键字的使用 代码一 package l2_for; //题目&#xff1a;输入两个正整数m和n&#xff0c;求…