当前位置: 首页 > news >正文

条款13:优先考虑const_iterator而非iterator

STL const_iterator等价于指向常量的指针(pointer-to-const)。它们都指向不能被修改的值。标准实践是能加上const就加上,这也指示我们需要一个迭代器时只要没必要修改迭代器指向的值,就应当使用const_iterator

上面的说法对C++11和C++98都是正确的,但是在C++98中,标准库对const_iterator的支持不是很完整。首先不容易创建它们,其次就算你有了它,它的使用也是受限的。假如你想在std::vector<int>中查找第一次出现1983(C++代替C with classes的那一年)的位置,然后插入1998(第一个ISO C++标准被接纳的那一年)。如果vector中没有1983,那么就在vector尾部插入。在C++98中使用iterator可以很容易做到:

std::vector<int> values;
…
std::vector<int>::iterator it =std::find(values.begin(), values.end(), 1983);
values.insert(it, 1998);

但是这里iterator真的不是一个好的选择,因为这段代码不修改iterator指向的内容。用const_iterator重写这段代码是很平常的,但是在C++98中就不是了。下面是一种概念上可行但是不正确的方法:

typedef std::vector<int>::iterator IterT;               //typedef
typedef std::vector<int>::const_iterator ConstIterT;std::vector<int> values;
…
ConstIterT ci =std::find(static_cast<ConstIterT>(values.begin()),  //caststatic_cast<ConstIterT>(values.end()),    //cast1983);values.insert(static_cast<IterT>(ci), 1998);    //可能无法通过编译,//原因见下

typedef不是强制的,但是可以让代码中的cast更好写。(你可能想知道为什么我使用typedef而不是条款9提到的别名声明,因为这段代码在演示C++98做法,别名声明是C++11加入的特性)

之所以std::find的调用会出现类型转换是因为在C++98中values是non-const容器,没办法简简单单的从non-const容器中获取const_iterator。严格来说类型转换不是必须的,因为用其他方法获取const_iterator也是可以的(比如你可以把values绑定到reference-to-const变量上,然后再用这个变量代替values),但不管怎么说,从non-const容器中获取const_iterator的做法都有点别扭。

当你费劲地获得了const_iterator,事情可能会变得更糟,因为C++98中,插入操作(以及删除操作)的位置只能由iterator指定,const_iterator是不被接受的。这也是我在上面的代码中,将const_iterator(我那么小心地从std::find搞出来的东西)转换为iterator的原因,因为向insert传入const_iterator不能通过编译。

老实说,上面的代码也可能无法编译,因为没有一个可移植的从const_iteratoriterator的方法,即使使用static_cast也不行。甚至传说中的牛刀reinterpret_cast也杀不了这条鸡。(它不是C++98的限制,也不是C++11的限制,只是const_iterator就是不能转换为iterator,不管看起来对它们施以转换是有多么合理。)不过有办法生成一个iterator,使其指向和const_iterator指向相同,但是看起来不明显,也没有广泛应用,在这本书也不值得讨论。除此之外,我希望目前我陈述的观点是清晰的:const_iterator在C++98中会有很多问题,不如它的兄弟(指iterator)有用。最终,开发者们不再相信能加const就加它的教条,而是只在实用的地方加它,C++98的const_iterator不是那么实用。

 所有的这些都在C++11中改变了,现在const_iterator既容易获取又容易使用。容器的成员函数cbegincend产出const_iterator,甚至对于non-const容器也可用,那些之前使用iterator指示位置(如inserterase)的STL成员函数也可以使用const_iterator了。使用C++11 const_iterator重写C++98使用iterator的代码也稀松平常:

std::vector<int> values;                                //和之前一样
…
auto it =                                               //使用cbeginstd::find(values.cbegin(), values.cend(), 1983);    //和cend
values.insert(it, 1998);

现在使用const_iterator的代码就很实用了!

唯一一个C++11对于const_iterator支持不足(译注:C++14支持但是C++11的时候还没)的情况是:当你想写最大程度通用的库,并且这些库代码为一些容器和类似容器的数据结构提供beginend(以及cbegincendrbeginrend等)作为非成员函数而不是成员函数时。其中一种情况就是原生数组,还有一种情况是一些只由自由函数组成接口的第三方库。(译注:自由函数free function,指的是非成员函数,即一个函数,只要不是成员函数就可被称作free function)最大程度通用的库会考虑使用非成员函数而不是假设成员函数版本存在。

举个例子,我们可以泛化下面的findAndInsert

 

template<typename C, typename V>
void findAndInsert(C& container,            //在容器中查找第一次const V& targetVal,      //出现targetVal的位置,const V& insertVal)      //然后在那插入insertVal
{using std::cbegin;using std::cend;auto it = std::find(cbegin(container),  //非成员函数cbegincend(container),    //非成员函数cendtargetVal);container.insert(it, insertVal);
}

它可以在C++14工作良好,但是很遗憾,C++11不在良好之列。由于标准化的疏漏,C++11只添加了非成员函数beginend,但是没有添加cbegincendrbeginrendcrbegincrend。C++14修订了这个疏漏。

如果你使用C++11,并且想写一个最大程度通用的代码,而你使用的STL没有提供缺失的非成员函数cbegin和它的朋友们,你可以简单的写下你自己的实现。比如,下面就是非成员函数cbegin的实现:

template <class C>
auto cbegin(const C& container)->decltype(std::begin(container))
{return std::begin(container);   //解释见下
}

你可能很惊讶非成员函数cbegin没有调用成员函数cbegin吧?我也是。但是请跟逻辑走。这个cbegin模板接受任何代表类似容器的数据结构的实参类型C,并且通过reference-to-const形参container访问这个实参。如果C是一个普通的容器类型(如std::vector<int>),container将会引用一个const版本的容器(如const std::vector<int>&)。对const容器调用非成员函数begin(由C++11提供)将产出const_iterator,这个迭代器也是模板要返回的。用这种方法实现的好处是就算容器只提供begin成员函数(对于容器来说,C++11的非成员函数begin调用这些成员函数)不提供cbegin成员函数也没问题。那么现在你可以将这个非成员函数cbegin施于只直接支持begin的容器。

如果C是原生数组,这个模板也能工作。这时,container成为一个const数组的引用。C++11为数组提供特化版本的非成员函数begin,它返回指向数组第一个元素的指针。一个const数组的元素也是const,所以对于const数组,非成员函数begin返回指向const的指针(pointer-to-const)。在数组的上下文中,所谓指向const的指针(pointer-to-const),也就是const_iterator了。 

回到最开始,本条款的中心是鼓励你只要能就使用const_iterator。最原始的动机——只要它有意义就加上const——是C++98就有的思想。但是在C++98,它(译注:const_iterator)只是一般有用,到了C++11,它就是极其有用了,C++14在其基础上做了些修补工作。

请记住:

  • 优先考虑const_iterator而非iterator
  • 在最大泛型代码中,优先考虑非成员函数版本的beginendrbegin等,而非同名成员函数

相关文章:

条款13:优先考虑const_iterator而非iterator

STL const_iterator等价于指向常量的指针&#xff08;pointer-to-const&#xff09;。它们都指向不能被修改的值。标准实践是能加上const就加上&#xff0c;这也指示我们需要一个迭代器时只要没必要修改迭代器指向的值&#xff0c;就应当使用const_iterator。 上面的说法对C11…...

23考研 长安大学846计算机考研复试《数据库》

长安大学846计算机考研,复试历年真题《数据库》。 目录: (1)数据库复习 (2)专业面试 (3)2017-2020年历年复试题 (4)复试的一些心得 数据库复习: 刚开始复试的时候,先把教材学习一遍(《数据库系统概念》 王珊 第五版),课后习题自己做一遍,网上有卖这本书的 配套…...

Android 9.0 系统去掉省电模式

1.概述 在9.0的系统rom开发定制化工作中,在系统中系统设置里面省电模式的选择中,有智能省电模式 省电模式 和超级省电模式三种 由于对rom系统做了大量定制功能开发,所以会在进入省电模式的时候 会出现某些不必要的问题,由于产品开发需求, 就要求去掉省电模式 不让平板进入…...

3 mmmmm

全部 答对 答错 单选题 7. 项目经理通知敏捷团队成员&#xff0c;由于意外的个人问题&#xff0c;产品负责人将不能参加即将到来的冲刺审查。这种情况下最可能的结果是什么&#xff1f; A project manager informs the agile team members that, due to unexpected personal …...

nvidia Jetson nano Linux内核编译

今天编译了nvidia 的jetson nano的内核。在网上找到的资料都比较老了。现在官网的最新版本是35.1.结合之前看到的博客的内容。关键是内核源码和交叉编译器的下载。找到官方文档后,编译成功!并且官方的文档是有一个编译脚本的。看之前的资料都是给出的命令,不知道这个nvbuild…...

理想汽车2023年销量冲击30万辆有戏吗?

出品 | 何玺 排版 | 叶媛 2月27日理想汽车发布财报&#xff0c;数据表明&#xff0c;2022年理想营收和销量双双大涨。在财报会议上&#xff0c;理想汽车CEO李想表示&#xff0c; 2023年的销量预期为30万辆。 那么&#xff0c;理想汽车2023年销量冲击30万辆的目标能实现吗&…...

借助CatGPT让turtlesim小乌龟画曲线

注意这里是CatGPT&#xff0c;不等同OpenAI的ChatGPT&#xff0c;但是用起来十分方便&#xff0c;效果也还行。详细说明ROS机器人turtlesim绘制曲线需要注意哪些ROS机器人turtlesim绘制曲线需要注意以下几点&#xff1a;绘制曲线前需要设置好turtlesim的初始位置和方向&#xf…...

Java面试总结(四)

synchroize的实例、静态、代码块的锁对象 修饰实例方法 修饰静态方法 修饰代码块 1、修饰实例方法 &#xff08;锁当前对象实例&#xff09; 给当前对象实例加锁&#xff0c;进入同步代码前要获得 当前对象实例的锁 。 synchronized void method() {//业务代码 }2、修饰静…...

强强联合,再强的英伟达NVIDIA也不落俗套

强强联合&#xff0c;全球科技领域永恒的话题【科技明说 &#xff5c; 每日看点】前些天&#xff0c;我看到GPU领域的英伟达(Nvidia)与微软(Microsoft)做了一项十年期的云计算协议&#xff0c;起初我以为微软Microsoft Azure与英伟达GPU方面有所合作&#xff0c;其实不然&#…...

maven使用心得

maven 配置文件默认在 ~/.m2/settings.xml maven命令行 mvn clean install -Dmaven.test.skiptrue -s ~/.m2/settings.xml 往本地仓库加jar包 命令形如&#xff1a; mvn install:install-file -DgroupIdcom.lee.net -DartifactIdMyToolIdl -Dversion1.0.0-SNAPSHOT -Dpac…...

【算法题】1958. 检查操作是否合法

插&#xff1a; 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你一个下标从 0 开始的 8 x 8 网…...

十一、GoF之代理模式

1 对代理模式的理解 【在程序中&#xff0c;对象A和对象B无法直接交互时。】 【在程序中&#xff0c;功能需要增强时。】 【在程序中&#xff0c;目标需要被保护时】 业务场景&#xff1a;系统中有A、B、C三个模块&#xff0c;使用这些模块的前提是需要用户登录&#xff0c;也…...

MySQL5.6和JVM(1.6)调优

MySQL5.6调优 目标了解什么是优化掌握优化查询的方法掌握优化数据库结构的方法掌握优化MySQL服务器的方法什么是优化?合理安排资源、调整系统参数使MySQL运行更快、更节省资源。<...

【汇编】三、寄存器(一只 Assember 的成长史)

嗨~你好呀&#xff01; 我是一名初二学生&#xff0c;热爱计算机&#xff0c;码龄两年。最近开始学习汇编&#xff0c;希望通过 Blog 的形式记录下自己的学习过程&#xff0c;也和更多人分享。 上篇系列文章链接&#xff1a;【汇编】二、预备知识&#xff08;一只 Assember 的…...

TFT通信协议解析与应用

TFTP&#xff08;Trivial File Transfer Protocol&#xff09;是一种简单的文件传输协议&#xff0c;常用于在本地网络上的设备之间传输小型文件。 通信大致过程 TFTP通信过程如下&#xff1a; TFTP通信双方建立连接&#xff1a;TFTP客户端与TFTP服务器建立连接。TFTP服务器监…...

python 操作word库docx 增强接口

前言用python 的docx 库操作word完成一些自动化的文档生成工作&#xff0c;但有时候会遇到docx库提供的操作无法直接满足业务上的需求&#xff0c;需要对其进行一些扩展。接口完善实现在指定的文字后面插入指定的文字任务&#xff1a;以下示例需要在文档中的所有 "人生苦短…...

ARM uboot 源码分析9 - uboot的硬件驱动部分

一、uboot 与 linux 驱动 1、uboot 本身是裸机程序 (1) 裸机本来是没有驱动的概念的&#xff08;狭义的驱动的概念就是&#xff0c;操作系统中用来具体操控硬件的那部分代码叫驱动&#xff09; (2) 裸机程序中是直接操控硬件的&#xff0c;操作系统中必须通过驱动来操控硬件…...

Mybatis动态sql语句foreach中拼接正则表达式字符串注意事项

今天要说到的查询情况&#xff0c;平时项目里边其实用到的并不是很多&#xff0c;使用正则表达式无非是为了匹配结果比较灵活&#xff0c;最常见的&#xff0c;我们的查询条件一般一个参数仅仅只是一种情况的筛选&#xff0c;对于如何选择查询方式&#xff0c;主要还是要看前端…...

JVM内置锁synchronized关键字详解

目录 JVM内置锁synchronized关键字详解 设计同步器的意义 如何解决线程并发安全问题&#xff1f; synchronized原理详解 synchronized底层原理 synchronized在jdk1.6前后的变化【重点】 jdk小于1.6时 jdk>1.6时 轻量级锁何时升级为重量级锁&#xff1f;&#xff1f;…...

【2021.12.25】xv6系统入门学习

【2021.12.28】为xv6系统添加一个开机密码 文章目录【2021.12.28】为xv6系统添加一个开机密码0、说明1、Ubuntu20上安装xv62、测试指令3、修改系统代码4、添加自己的程序命令0、说明 xv6 是 MIT 设计的一个教学型操纵系统。 记录Ubuntu上安装x86版本的xv6系统&#xff0c;为其…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...