STL 迭代器萃取

news2025/1/20 7:22:05

导言

什么是迭代器

迭代器是一种抽象的设计概念,《Design Patterns》一书中对于 iterator 模式的定义如下:提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表述方式。

为什么需要迭代器萃取

有时在我们使用迭代器的时候,很可能会用到其相应型别(associated type)。什么是相应型别,迭代器所指之物的型别、所属的类型(随机迭代器、单向双向、只读只写)便是。

如果我们想要以迭代器所指之物型别为类型声明一个变量,该怎么办呢?

一种解决方法是:利用 function template 的参数推倒(argument deducation)机制。

例如:

template <class I, class T>
void func(I iter, T t) {
	T tmp;	// 成功声明了迭代器所指之物类型的变量
}

template <class I>
void func(I iter) {
	func_impl(iter, *iter);
}

int main() {
	int num = 0;
    func(&num);
}

但迭代器的型别不只是迭代器所指对象的型别,而且上述解法并不能用于所有情况,因此需要更加全面的解法。

比如上述解法就无法解决 value type 用于函数返回值的情况,毕竟推导的只是参数,无法推导返回值型别。

声明内嵌类型似乎是个很好的方法,像这样:

template <class T>
struct MyIter {
	typedef T value_type;
    T* ptr;
    
    MyIter(T* p) {
        ptr = p;
    }
    T& operator*() { 
        return *ptr; 
    }
};

template <class I>
typename I::valie_type func (I ite) {	// typename I::valie_type 为返回值类型
	return *ite;
}

MyIter<int> ite(new int(1231));
cout << func(ite) << endl;

此处 typename 的作用是告诉编译器这是一个类型,因为 I 是一个模板参数,在它被具现化之前编译器对它一无所知,也就是说编译器不知道 I::valie_type 是个类型或是成员函数等等。

更多关于 typename 的用法可以看这个链接:typename 的用法

还有一种情况是上述代码无法解决的,那就是不是所有的迭代器都是 class type,原生指针就不是。如果不是 class type 就无法为它定义内嵌型别,因此我们需要对原生指针作些特殊处理。

例如:

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

此时,不管是 class type 类型的迭代器还是原生指针都可以处理了。

迭代器萃取,就是为我们榨取出迭代器的相应型别。当然,要使萃取有效的运行,每个迭代器都要自行以内嵌性别定义(nested typedef)的方式定义出相应型别。

最常用的迭代器型别有五种:value type,difference type,pointer,reference,iterator catagoly。

迭代萃取机 traits 会很忠实地将它们榨取出来:

template <class I>
struct iterator_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;
};

iterator_traits 必须针对传入型别为 point 及 point-to-const 者,设计特化版本。

value type

value type 是指迭代器所指对象的型别。

做法如上文所述。

difference type

difference type 用来表示两个迭代器之间的距离。对于连续空间的容器来说,头尾之间的距离就是最大容量,因此它也可以用来表示一个容器的最大容量。

如果一个泛型算法提供记数功能,例如 STL 的 count(),其返回值就必须使用迭代器的 difference type:

template<class I, class T>
typename iterator_traits<I>::difference_type		// 返回值类型,实际是 I::difference type
    count(I first, I last, const T& value) {
    typename iterator_traits<I>::difference_type ret = 0;
    for (; first != last; ++first) {
        if (*first == value) {
            ret++;
        }
    }
    return ret;
}

针对相应型别 difference type,traits 的两个特化版本,以 C++ 内建的 ptrdiff_t 作为原生指针的 difference type。

template <class I>
struct iterator_traits {
	typedef typename I::difference_type difference_type;
};
template <class T>
struct iterator_traits<T*> {
	typedef ptrdiff_t difference_type;
};
template <class T>
struct iterator_traits<const T*> {
	typedef ptrdiff_t difference_type;
};

reference type

从迭代器所指之物的内容是否允许改变的角度来说,迭代器分为两种:不允许改变所指对象的内容者,称为 constant iterators;允许改变所指对象的内容者,称为 mutable iterators。当我们对允许改变内容的迭代器进行解引用操作时,获得的不应是一个右值,应该是一个左值,因为右值不允许赋值操作。

在 C++ 中,函数如果要传回左值,都是以引用的方式进行。所以当 p 是个 mutable iterators 时,如果其 value type 是 T,那么 *p 的型别不应该是 T,而应是 T&。同样的,如果 p 是一个 constant iterators,其 value type 是 T,那么 *p 的型别不应该是 const T,而应该是 const T&。实现将在下一部分给出。

point type

同样的问题也出现在指针这里,能否改变所指地址的内容,影响着取出的指针类型。

实现如下:

template <class I>
struct iterator_traits {
	typedef typename I::pointer 	pointer;
    typedef typename I::reference	reference;
};
template <class T>
struct iterstor_traits<T*> {
	typedef T* 	pointer;
    typedef T& 	reference;
};
template <class T>
struct iterstor_traits<const T*> {
	typedef const T*	pointer;
    typedef const T& 	reference;
};

iterator_category

根据移动特性与施行操作,迭代器被分为五类:

请添加图片描述

前三种支持 operator++,第四种再加上 oprerator--,最后一种则涵盖所有指针算术能力。

这些迭代器的分类与从属关系,可以用下图表示。直线与箭头并非表示继承关系,而是所谓概念与强化的关系。更类似于,随机迭代器是一个双向迭代器,双向迭代器也是一个单向迭代器的概念。
请添加图片描述

设计一个算法时,要尽可能针对图中某种迭代器提供一个明确定义,并针对更加强化的某种迭代器提供另一种定义,这样才能在不同情况下提供最大效率。

以 distance() 为例

distance() 函数用来计算两个迭代器之间的距离。针对不同的迭代器类型,它可以用不同的计算方式,带来不同的效率。

template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
    __distance(InputIterator first, InputIterator last,
              input_iterator_tag) {
	iterator_traits<InputIterator>::iteratordifference_type n = 0;
    // 逐一累计距离
    while (first != last) {
		++first;
        ++n;
    }
    return n;
}

template <class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
    __distance(RandomAccessIterator first, RandomAccessIterator last,
               random_access_iterator_tag) {
    // 直接计算差距
    return last - first;
}

// InputIterator 命名规则:所能接受的最低阶迭代器类型
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
    distance(InputIterator first, InputIterator last) {
	typedef typename iterator_traits<InputIterator>::iterator_category category;
    return __distance(first, last, category());
}

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

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

相关文章

wcdma基站的重选和切换

首先移动组网的特点&#xff0c;单个基站覆盖一定区域范围&#xff0c;我们称之为小区&#xff0c;为了组成一个连续服务不断的网&#xff0c;需要在空间上部署多个基站&#xff0c;应对用户的移动。 为了实现业务的连续性&#xff0c;必须给每个小区设置相邻的小区&#xff0c…

C/C++-指针

C/C-指针参考1. 指针指针与内存/地址指针使用2.数组指针数组/指针/sizeof一维数组与指针多维数组指针3.字符/字符串指针4. 其他指针二级指针 -- 还没看空指针void指针野指针5.指针与函数函数指针引用传递指针函数6.结构体指针结构体数组指针???c 对象指针参考 https://www.…

Kotlin 开发Android app(十四):startActivity跳转Activity活动

从一个Activity跳到另一个Activity 在编写安卓代码是最常见的事情了。我们不可能在一个页面中&#xff0c;把所有的事情都做完。 在kotlin中跳转的程序也比较简单&#xff0c;跟java差不多。如果熟悉java代码的话&#xff0c;只需要把代码改变过来而已。 带参数的调整 从一个…

极空间Docker安装Alist套件整合阿里云盘、百度云盘等网盘资源并挂载到本地供极影视刮削播放完整教程

文章目录0、前言1、在docker中安装alist套件1.1、拉取并下载alist镜像1.2.安装alist镜像2、访问并设置alist2.1、访问alist2.2、配置alist2.2.1、在alist中添加阿里云盘2.2.2、在alist中添加其它网盘3、在极空间中将前述网盘挂载到本地4、在极影视中扫描刮削挂载到本地的云盘中…

如何制作一个微信小程序【微信小程序是怎么做的】

为什么现在这么多人使用微信小程序呢&#xff1f;因为微信小程序除了便捷易开发&#xff0c;公司企业可以用来做小程序展示官网&#xff0c;商家也可以做小程序商城&#xff0c;甚至个人也可以拥有自己的小程序。那么如何制作一个微信小程序&#xff1f;微信小程序是怎么做的呢…

第二证券|房地产股债嗨了,百余只个股谁受热捧?谁还受益?

29日&#xff0c;AH股房地产板块狂飙&#xff0c;地产债反常火热。 A股地产股开盘即掀涨停潮&#xff0c;中国武夷、中交地产、空港股份、光大嘉宝、中华企业等超10股竞价涨停。港股内房股大幅高开&#xff0c;碧桂园、富力地产、新城开展等多股涨超10%&#xff0c;盘中三巽集…

Crack:Stimulsoft BI Server 2022.4.5

Stimulsoft BI Server 是一个客户端-服务器系统&#xff0c;可让您高效且有效地实施使用报告和仪表板的完整周期&#xff0c;从执行信息处理任务的设计和自动化开始&#xff0c;到为方便地向用户展示结果做准备结束。报告模块的功能和功能是使用 Stimulsoft 的快速现代技术实现…

一、Vue3基础[组件(props、事件、插槽)]

一、组件化 解释:正如上图所示,一个页面可以分为多块部分,但是如果把所有代码都写在一个vue文件当中,维护性和可读性都会很差,所以需要用到组件化思维->创建多个vue文件每个里面写一部分代码,然后集中在一个主的vue文件调用 二、组件的注册 1.全局 解释:顾名思义,…

【Java盲点攻克】「时间与时区系列」让我们一起完全吃透对于时区和日期相关的功能开发原理

技术简介 java中的日期处理一直是个问题&#xff0c;没有很好的方式去处理&#xff0c;所以才有第三方框架的位置比如joda。文章主要对java日期处理的详解&#xff0c;用1.8可以不用joda。 时间概念 首先我们对一些基本的概念做一些介绍&#xff0c;其中可以将GMT和UTC表示时…

自然算法 - AI面试基础补全

手撕BP神经网络手写Bert和Transformer&#xff08;BERT很细节的地方&#xff0c;比如文字标签CLS&#xff0c;par&#xff09;学习pytorch&#xff0c;tensorflow AI算法岗位 可看网站 牛客网站 面经回复 github 项目连接 算法工程师岗位必备知识 问答 ELMO、GPT、…

Linux系统下KVM虚拟机的基本管理和操作

Linux系统下KVM虚拟机的基本管理和操作一、检查本地环境1.检查系统版本2.检查防火墙状态3.检查selinux3.检查libvirtd服务状态4.检查kvm安装结果5.检查kvm虚拟机状态6.检查virsh版本二、virsh常用命令1.列出虚拟机2.虚拟机开关机操作3.删除虚拟机4.设置虚拟机在宿主机开机时自启…

装在笔记本里的私有云环境:K8s 集群准备

本篇是系列中的第六篇内容&#xff0c;继续聊聊如何把一个简化过的私有云环境部署在笔记本里&#xff0c;以满足低成本、低功耗、低延时的实验环境。 在前几篇内容中&#xff0c;我们聊过了&#xff1a;虚拟化、监控、基础的存储、持续集成等内容&#xff0c;接下来的内容中&a…

告诉你如果对一个新产品进行测试

初入一家公司&#xff0c;当一个全新的产品摆在你的面前&#xff0c;你会如何快速入手呢&#xff1f;点、点、点。。。虽说实践是熟悉系统的第一要素&#xff0c;但我们需要静静思考一下。我是谁&#xff1f;--QA我在哪&#xff1f;--**产品组我要做什么&#xff1f;--保质量有…

Nature:“我还有用!“凋亡细胞释放的代谢物充当组织信使

代谢组学文献分享&#xff0c;细胞凋亡是指为维持内环境稳定&#xff0c;由基因控制的细胞自主的有序的死亡。2002年授予在“发现细胞凋亡的重要调控分子并阐述其作用机制”方面做出重要贡献的三位科学家诺贝尔生理和医学奖&#xff0c;caspase依赖的细胞凋亡约占机体内稳态细胞…

多线程初阶(一)

目录 前言&#xff1a; 认识多线程 创建线程 run方法和start区别 继承Thread类 实现Runnable接口 匿名内部类实现继承Thread类 匿名内部类实现Runnable接口实例 Lambda表达式 中断线程 等待线程 线程休眠 线程状态 线程状态之间切换 代码观察线程的状态 线程安…

多点DMALL × Apache Kyuubi:构建统一SQL Proxy探索实践

伴随着国家产业升级的推进和云原生技术成熟&#xff0c;多点 DMALL 大数据技术也经历了从存算一体到存算分离的架构调整变迁。本文将从引入 Kyuubi 实现统一 SQL Proxy 的角度讲述这一探索实践的历程。 多点 DMALL 成立于2015年&#xff0c;提供一站式全渠道数字零售解决方案 D…

STL的常用算法-查找 (20221130)

STL的常用算法 概述&#xff1a; 算法主要是由头文件<algorithm> <functional> <numeric> 组成。 <algorithm>是所有STL头文件中最大的一个&#xff0c;涉及比较、交换、查找、遍历等等&#xff1b; <functional>定义了一些模板类&#xff0…

使用记账软件记录生活收支明细,如何防止收支不被他人修改

坚持记账是每个人都必须要做的事情&#xff0c;日常生活中的生活开支都是一笔笔的支出&#xff0c;一个月挣来的工资&#xff0c;在不知不觉之中就花完了&#xff0c;可以使用——晨曦记账本记录生活明细&#xff0c;为了防止被他人修改&#xff0c;该如何操作呢&#xff1f;一…

Allegro调丝印规范操作指导

Allegro调丝印规范操作指导 Allegro和其它PCB设计软件一样,丝印的排布也是类似的,具体规范介绍如下 以下图为例 打开Setup-Design Parameter 选择text 设置丝印字体的参数 设置需要丝印的字体,比如3号字体,参数如下 然后点击OK Edit-Change需要调整丝印的字体

Linux-Hadoop部署

部署Hadoop一、Hadoop部署模式1、独立模式2、伪分布式模式3、完全分布式模式二、Hadoop集群规划1、集群拓扑2、角色分配三、JDK安装与配置1、下载JDK压缩包2、上传到master虚拟机3、在master虚拟机上安装配置JDK4、将JDK分发到slave1和slave2虚拟机5、将环境配置文件分发到slav…