c++ stl 迭代器iterators(traits编程技法)

2023-05-16

文章目录

    • 1.1 迭代器设计思维-stl关键所在
    • 1.2 迭代器是一种smart pointer
    • 1.3 迭代器相应型别(associated types)
    • 1.4 traits编程技法-stl源代码门匙
      • 1.4.1 value type
      • 1.4.2 different type
      • 1.4.3 reference type
      • 1.4.4 pointer type
      • 1.4.5 iterator_category
    • 1.5 std::iterator的保证
    • 1.6 iterator源代码完整重列

iterator模式定义如下:
提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表达方式。

1.1 迭代器设计思维-stl关键所在

stl的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶着剂将他们撮合在一起。容器和算法的范型化,从技术角度来看并不困难,c++的class templates和function templates可分别达成目标。如何设计出两者之间的良好胶着剂,才是大难题。

1.2 迭代器是一种smart pointer

迭代器是一种行为类似指针的对象(实际上是结构体模板),而指针的各种行为中最常见也最重要的便是内容提领(dereference)和成员访问(member access),因此,迭代器最重要的编程工作就是对operator*和operator->进行重载(overloading)工作。
… …
把迭代器的开发工作交给容器的设计者,如此一来,所有实现细节反而得以封装起来不被使用者看到。这正是为什么每一种stl容器都提供专属迭代器的缘故。

1.3 迭代器相应型别(associated types)

什么是相应型别?迭代器所指之物的型别便是其一。c++只支持sizeof(),并未支持typeof()。即便动用RTTI性质中的typeid(),获得的也只是型别名称,不能拿来做变量声明之用
解决办法是:利用function template的参数(argument deducation)推导机制。
在这里插入图片描述

1.4 traits编程技法-stl源代码门匙

参数型别推导技巧虽然可用于value type,却非全面可用:万一value type必须用于函数的传回值,就束手无策了,毕竟函数的“(法1)template 参数推导机制”推而导之的只是参数,无法推导函数的返回值类别。“(法2)声明内嵌型别”似乎是个好主意,就这样:
在这里插入图片描述
如果不是class type,就无法为它定义内嵌型别。
(法3)template partial specialization偏特化”针对任何template参数更进一步的条件限制所设计出来的一个特化版本。
在这里插入图片描述
有了这项利器,我们便可以解决前述“内嵌型别”未能解决的问题。
class template专门用来“萃取”迭代器的特性,而value type正是迭代器的特性之一:

template <class I>
struct iterator_traits 
{
	typedef typename I::value_type value_type;
};

这个所谓的traits,其意义是,如果I定义有自己的value_type,那么通过这个traits的作用,萃取出来的value_type就是I::value_type。换句话说,如果I定义有自己的value_type,先前那个func()可以改写成这样:
在这里插入图片描述针对原生指针而设计的偏特化版在这里插入图片描述如果迭代器是个pointer-to-const,设法令其value type为一个non-const型别。没问题,只要另一个特化版本,就能解决这个问题:
针对原生的pointer-to-const而设计的偏特化版
在这里插入图片描述说明了traits所扮演的“特性萃取机”角色,萃取各个迭代器的特性。这里所谓的迭代器特性,指的是迭代器的相应型别(assiociated types)。当然,若要这个“特性萃取机”traits能够有效运作,每一个迭代器必须遵循约定,自行以内嵌型别定义(nested typedef)的方式定义出相应型别(associated types)。这是一个约定,谁不遵守这个约定,谁就不能兼容与
stl这个大家庭。
在这里插入图片描述根据经验,最常用到的迭代器相应型别有五种:value_type,different type,pointer,refernece,iterator categoly。如果你希望你所开发的容器能与stl水乳交融,一定要为你的容器的迭代器定义这五种相应型别。“特性萃取机”traits会很忠实地将原汁原味榨取出来:

template <class I>
struct iterator_traits
{
	typedef typename I::iterator_category iterator_category;
	typedef typename I::value_type        value_type;
	typedef typename I::different_type    different_type;
	typedef typename I::pointer           pointer;
	typedef typename I::reference         reference;
};

1.4.1 value type

所谓value type,是指迭代器所指对象的型别。任何一个打算与stl算法有完美搭配的class,都应该定义自己的value type内嵌型别。

1.4.2 different type

different type用来表示两个迭代器之间的距离,因此它也可以用来表示一个容器的最大容量,因为对于连续空间的容器而言,头尾之间的距离就是其最大容量。如果一个范型算法提供计数功能,例如stl的count(),其传回值就必须使用迭代器的different type:

template <class I,class T>
typename iterator_traits<I>::difference_type 
count(I first,I last,const T& value)
{
	typename iterator_traits<I>::difference_type n = 0;
	for(;first != last;++first)
		if(*first == value)
			++n;
	return n;
}

针对相应型别difference type,traits的如下两个(针对原生指针而写的)特化版本,以c++内建的ptrdiff_t作为原生指针的difference type:

template <class I>
struct iterator_traits
{
	...
	typedef typename I::difference_type difference_type;
};

// 针对原生指针而设计的“偏特化(partial specialization)”版

template <class T>
struct iterator_traits<T*>
{
	...
	typedef ptrdiff_t difference_type;
}

// 针对原生的pointer-to-const而设计的“偏特化(partial specialization)”版

template <class T>
struct iterator_traits<const T*>
{
	...	
	typedef ptrdiff_t difference_type;	
}

1.4.3 reference type

从“迭代器所指之物的内容是否允许改变”的角度观之,迭代器分为两种:不允许改变“所指对象之内容”者,称为constant iterastors,例如const int* pic;允许改变“所指对象之内容”者,称为mutable iterators,例如int* pi。当我们对一个mutable iterators进行提领操作时,获得的不应该是一个右值(rvalue),应该是一个左值(lvalue),因为右值不允许赋值操作(assignment)左值才允许。

在c++中,函数如果要传回左值,都是以by reference的方式进行,所以当p是个mutable iterators时,如果其value type是T,那么p的型别不应是T,应该是T&。将此道理扩充,如果p是一个constant iterastors,其value type是T,那么p的型别不是const T,而应该const T&。这里所讨论的*p的型别,即所谓的reference type。

1.4.4 pointer type

pointers和references在c++中有非常密切的关联。如果“传回一个左值,令它代表p所指之物”是可能的,那么“传回一个左值,令他代表p所指之物的地址”也一定可以。也就是说,我们能够传回一个pointer,指向迭代器所指之物。现在我们把reference type和pointer type这两个相应型别加入traits内:

template <class I>
struct iterator_traits
{
	...
	typedef typename I::pointer pointer;
	typedef typename I::reference reference;
};

// 针对原生指针而设计的“偏特化(partial specialization)”版

template <class T>
struct iterator_traits<T*>
{
	...
	typedef T* pointer;
	typedef T& reference;
}

// 针对原生的pointer-to-const而设计的“偏特化(partial specialization)”版

template <class T>
struct iterator_traits<const T*>
{
	...
	typedef const T* pointer;
	typedef const T& reference;
}

1.4.5 iterator_category

迭代器的分类:
·Input Iterator:只读(read only)。
·Output Iterator:只写(write only)。
·Forward Iterator:允许“写入型”算法(例如replace())在此种迭代器所形成的区间上进行读写操作。
·Bidirectional Iterator:可双向移动。
·Ramdom Access Iterator:前四种迭代器都只供应一部分指针算数能力(前三种支持operator++,第四种再加上operator–),第五种则涵盖所有指针算数能力,包括p+n,p-n,p[n],p1-p2,p1<p2。

concept(概念)与refinement(强化)的关系
在这里插入图片描述设计算法时,如果可能,我们尽量针对图中的某种迭代器提供一个明确定义,并针对更强化的某种迭代器提供另一种定义,这样才能在不同情况下提供最大效率。

1.5 std::iterator的保证

为了符合规范,任何迭代器都应该提供五个内嵌相应型别,以利于traits萃取,否则便是自别于整个stl架构,可能无法与其他stl组件顺利搭配。然而写代码难免挂一漏万,谁也不能保证不会有粗心大意的时候。如果能够将事情简化,就好多了。stl提供了一个iterator class如下,如果每个新设计的迭代器都继承自它,就可保证符合stl所需之规范:

template <class Category,
          class T,
	      class Distance = ptrdiff,
	      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;
};

iterator class不含任何成员,纯粹只是型别定义,所以继承它并不会招致任何额外负担。由于后三个参数皆有默认值,故新的迭代器提供前两个参数即可。
在这里插入图片描述

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

总结:
设计适当的相应型别(associated types),是迭代器的责任。设计适当的迭代器,则是容器的责任。唯容器本身,才知道该怎样设计出怎样的迭代器来遍历自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员…)。至于算法,完全可以独立于容器和迭代器之外自行发展,只要设计时以迭代器为对外接口就行。
traits编程技法大量运用于stl实现品中。它利用“内嵌型别”的编程技巧与编译器的template参数推导功能,增强c++未能提供的关于型别认证方面的能力,弥补c++不为强型别(strong typed)语言的遗憾。

1.6 iterator源代码完整重列

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  differece_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::differece_type    differece_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                  differece_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                  differece_type;
    typedef T*                         pointer;
    typedef T&                         reference;
};
... ...
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++ stl 迭代器iterators(traits编程技法) 的相关文章

  • unicode 与UTF8

    utf 8 编码是在传输中或者保存时候是字节码 xff0c 没有大小端的问题 以存储为例 xff1a 不管是英文 xff08 1个字节 xff09 还是中文 xff08 3个字节 xff09 xff0c 不用考虑大小端问题 xff0c 直接
  • sip register超时和invite超时

    sip的register重传与超时 当发送第一个register时候 xff0c 开启定时器 xff0c 超过500ms没有响应 xff0c 重新发送 在发送的register 1s内没有响应 xff0c 在重发 xff0c 一致到32s的
  • 线性地址转物理地址

    二 线性地址转物理地址 前面说了Linux中逻辑地址等于线性地址 xff0c 那么线性地址怎么对应到物理地址呢 xff1f 这个大家都知道 xff0c 那就是通过分页机制 xff0c 具体的说 xff0c 就是通过页表查找来对应物理地址 准
  • H264/H265 PS 流分析

    对于第一帧的第一个包开始为PS头 43 系统头 43 节目映射流 1 Pack start code xff1a 包起始码字段 xff0c 值为0x000001BA的位串 xff0c 用来标志一个包的开始 2 System clock re
  • linux c/c++ 程序崩溃时打印调用栈

    本文章是在总结前人的帖子后实现的 xff0c 经过验证是可以使用的 但是在个别信号下 xff0c 还是不能详细的打印有效信息 void dump void char szLog MAX LOG LEN 61 0 int j nptrs co
  • sip stale

    ii Stale 一个标志 xff0c 用来指示客户端先前的请求因其nonce值过期而被拒绝 如果stale是TRUE xff08 大小写敏感 xff09 xff0c 客户端可能希望用新的加密回应重新进行请求 xff0c 而不用麻烦用户提供
  • C++中模板类的输入输出流重载函数实现

    转 C 43 43 模板类中操作符重载之 34 lt lt 34 和 34 gt gt 34 重载 2011 01 10 17 05 31 分类 xff1a IT与程序 标签 xff1a 字号 大 中 小 订阅 在模板类中输入流 和输出流
  • SIP注册过程

    SIP的注册过程很容易理解 首先我有一个号码 xff0c 但我的号码可以移动的话 xff0c 那服务器如何才能找到我呢 xff1f SIP的注册机制将sip终端的位置报告给注册服务器 这里的注册服务器仅仅是一个逻辑角色 并不一定是独立的物理
  • linux ls命令 目录下文件太多

    在查看一个文件目录下有哪些文件的时候 xff0c 有时候目录下文件太多 所以需要如下命令筛选 ls less 之后按下ctrl 43 F是下一页 xff0c 按下ctrl 43 B是上一页 xff0c 按下q是回去 有时候文件夹内文件数量太
  • protected 构造方法

    使用protected 构造方法 xff0c 是包保护措施 xff0c 出了这个包 xff0c 在别的包里面是不可以new这个对象的 假设class A 的构造函数是protected的 如果在别的包里还想用我这个类的功能 xff0c 只能
  • 简易小工具实现批量打开多个网页

    最近有个需求 xff0c 希望一次性可以打开多个网页 xff0c 网址自由指定 xff0c 这个需求的实现非常简单 xff0c 使用基本的c 43 43 代码调用system函数即可 xff0c 都不需要MFC相关的东西 但是我实测一些工具
  • STM32硬件复位后无法启动

    最近遇到一个很奇怪的问题 xff0c 把STM32的reset脚拉低后再拉高 xff0c 也就是所谓的硬件复位 xff0c 按道理来说 xff0c STM32应该重启才对 xff0c 可是并没有 xff0c 连main函数都没进去 xff0
  • c++ 除号 /

    在c 43 43 中 xff0c 经常会用到整数的除法 xff0c 但是值得注意的是整数的除法是有数据丢失的 整数除法的余数直接被丢弃 xff0c 只取了商的整数部分 不是四舍五入
  • SIP 鉴权 & HTTP 认证

    sip 鉴权是基于摘要签名认证的 具体来说 每一个用户都有一个用户名和密码 用户名和密码在客户端和SIP 服务器的数据库中都有保存 在认证的过程中 客户端将自己的信息 用户名 密码 url 等信息 做一些复杂的MD5 或者SHA256 SH
  • c++中的extern c以使用

    extern C 是c 43 43 可以正确使用c中代码而产生的 xff0c 虽然c 43 43 兼容c xff0c 但是在c 43 43 程序调用c的库时 xff0c 也会产生链接错误 因为c的库中函数的修饰规则与c 43 43 的函数修
  • 【C++】类与结构体的区别

    C 43 43 中结构体 xff08 struct xff09 我们知道C 43 43 中的 struct 对C中的 struct 进行了扩充 xff0c 它不再是只能用来封装不同类型数据的数据结构了 xff0c 而是拥有了更多的功能 xf
  • C++模板类成员函数最好和模板类声明一起放在同一个.h头文件里

    一个完整的C 43 43 程序应包括三部分 头文件 包含结构声明和使用这些结构的函数原型 头文件常包含的内容如下所示 xff1a 1 xff09 函数原型 2 xff09 使用 define或const定义的符号常量 3 xff09 结构声
  • 【VSCode】Visual Studio Code软件使用入门

    说明 这是一篇VS Code IDE软件使用入门文章 xff0c 工欲善其事 xff0c 必先利其器 xff0c 在使用新IDE之前 xff0c 不妨先对其进行一个全面了解 如果你做WPF等桌面端开发 xff0c 目前VSCode还没有好用
  • Elasticsearch 按字段进行分组 aggs 聚合 分组

    ES 按 userName 字段进行分组 统计 34 query 34 34 bool 34 34 must 34 34 range 34 34 operateTime 34 34 gt 34 34 2020 05 18 00 00 00
  • Hutool 操做excel 导出大数据 到excel

    1 hutool的版本 xff1a hutool all 4 5 15 2 POI 的版本 xff1a 3 17 lt dependency gt lt groupId gt org apache poi lt groupId gt lt

随机推荐

  • 使用vue-router携带不同参数多次push到一个页面时请求 不重新触发问题 ,只有第一次触发

    1 vue跳转 this router push path 39 user userDetils 39 query id JSON stringify val id name JSON stringify this searchData n
  • 惯性导航坐标系介绍

    常用坐标系定义 运载体中三维空间运动包含六个自由度 xff0c 既有角运动也有线运动 在地球表面附近 xff0c 运载体的角运动描述一般以当地水平面和地理北向为参考基准 xff1b 线运动的描述通常采用地理经度 纬度和高度表示 xff0c
  • 达梦 DM管理工具

    DM 管理工具是数据库自带的图形化工具 xff0c 可以方便快捷的对数据进行管理 在网络允许的条件下 xff0c 可通过单个管理工具 xff0c 对多个数据实例进行管理 xff0c 方便简化 DBA 对数据库的日常运维操作要求 打开DM管理
  • Windows 下 DM 的安装 和 数据库配置工具使用说明

    步骤 1 xff1a 运行安装程序 用户将 DM 安装光盘放入光驱中 xff0c 插入光盘后安装程序自动运行或直接双击 setup exe 安装程序后 xff0c 程序将检测当前计算机系统是否已经安装其他版本 DM 如 果存在其他版本 DM
  • window 下 达梦数据库的备份和还原

    DM 提供的各种工具进行备份还原与恢复的操作 xff0c 包括 DIsql工具 DMRMAN 工具 图形化客户端管理工具 MANAGER 和 CONSOLE DIsql 工具用于执 行联机的数据备份与数据还原 xff0c 包括数 归档备份据
  • Linux下与Windows的文件共享

    有三种方法 安装VMware Tools xff08 在虚拟机 gt 重新安装VMware Tools xff09 通过Winscp软件 xff08 前提Windows能ping通linux xff0c 和关防火墙 xff09 本文介绍 x
  • 关于大小端的经典问题

    源代码如下 xff1a span class hljs preprocessor include lt stdio h gt span span class hljs keyword int span main span class hlj
  • 关于#define宏的生命周期

    我们一起来看一段代码 xff1a span class hljs preprocessor include lt stdio h gt span span class hljs preprocessor define X 3 span sp
  • 关于char的溢出问题

    现在看下面的问题 span class hljs keyword int span main span class hljs keyword char span number 61 span class hljs number 129 sp
  • exit()和_exit()的区别

    exit c源代码 xff1a span class hljs preprocessor include span span class hljs preprocessor include span span class hljs keyw
  • ubuntu16.04下u盘的自动挂载(脚本)

    一般固定的u盘在 dev sdxx 的形式 先在 mnt下建一个usb目录用于挂载 1 在 etc udev rules d下创建10 usb rules文件 xff0c 内容如下 xff1a SUBSYSTEM 61 61 34 bloc
  • arp欺骗

    ARP工作的过程 原理及现象 ARP全称是地址解析协议 xff08 address resolution potocol xff09 xff0c 是在仅仅知道主机的IP地址时确定其物理的地址的一种协议 ARP协议的工作过程 场景 xff1a
  • LeetCode中stdout结果是正确的,输出没有

    没有返回值 xff0c 加一行return
  • gstreamer学习(一) gstreamer-rtsp-server环境安装

    gstreamer rtsp server环境安装 Linux环境下 两种方式 xff1a 第一种方式 xff0c 通过官网安装 xff08 如果是Linux环境 xff0c 可以直接通过软件包工具进行安装 xff09 xff0c 点击进入
  • 用C++打开指定网址

    用C 43 43 打开指定网址原理 system 命令 就像这样 xff1a span class token macro property span class token directive hash span span class t
  • 项目遇到的各种异常抛出及解决方法

    项目遇到的各种异常抛出及解决方法 xff1a 1 java lang NumberFormatException xff1a 类型格式异常 第一次遇到的异常抛出原因及解决方法 xff1a 项目运行没有问题 xff0c 各种接口能正常查询出数
  • 【STC8学习笔记】STC8A8K64S4A12精准延时函数设置

    在设置单片机精准的延时函数的时候 xff0c 给大家一个方法 xff0c STC ISP有一个延时函数计算器 xff0c 可以计算出想要的延时 我的例程也是基于这个软件生成的 xff0c 我生成一个1ms和1us出来 xff0c 剩下的我再
  • vc版本与vs版本对应关系

    vc版本与vs版本对应关系 最近在整理之前代码 xff0c 用cmake编译一直报错 xff0c 忘记了opencv3 1 0不支持vs2019 xff0c 所以在这里总结下vc版本与vs版本对应关系 VC版本号 VS对应版本 vc6 VC
  • cmake编译依赖opencv的c++库

    前面一篇主要讲了c 43 43 项目怎么在本地配置opencv过程 xff0c 这种方式缺点就是只能在开发着本地环境编译 xff0c 换台电脑就会出现环境配置问题 接下来主要讲解 xff0c 使用cmake编译 xff0c 生成一个依赖op
  • c++ stl 迭代器iterators(traits编程技法)

    文章目录 1 1 迭代器设计思维 stl关键所在1 2 迭代器是一种smart pointer1 3 迭代器相应型别 xff08 associated types xff09 1 4 traits编程技法 stl源代码门匙1 4 1 val