当前位置: 首页 > 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;为其…...

Linux内核4.14版本——drm框架分析(4)——crtc分析

目录 1. struct drm_crtc结构体 2. crtc相关的API 2.1 drm_crtc_init_with_planes 2.5 drm_mode_setcrtc 3. func的一些介绍 3.1 struct drm_crtc_helper_funcs 3.2 struct drm_crtc_funcs 4. 应用层的调用 4.1 drmModeSetCrtc &#xff08;drmlib库里&#xff09;---…...

用原生js手写分页功能

分页功能如下&#xff1a; 数据分页显示&#xff0c;每页显示若干条数据&#xff0c;默认当前页码为第一页。例如&#xff1a;每页5条数据&#xff0c;则第一页显示 1-5 条&#xff0c;第二页显示 6-10 条&#xff0c;依此类推。当页码为第一页时&#xff0c;上一页为禁用状态…...

CornerNet介绍

CornerNet: Detecting Objects as Paired Keypoints ECCV 2018 Paper&#xff1a;https://arxiv.org/pdf/1808.01244v2.pdf Code&#xff1a;GitHub - princeton-vl/CornerNet 摘要&#xff1a; 提出了一种single-stage的目标检测算法CornerNet&#xff0c;它把每个目标检…...

【SpringBoot】日志使用

默认配置 Spring Boot默认帮我们配置好了日志 //记录器Logger logger LoggerFactory.getLogger(getClass());Testpublic void contextLoads() {//System.out.println();//日志的级别&#xff1b;//由低到高 trace<debug<info<warn<error//可以调整输出的日志级…...

关于slice扩容性能损耗的探究

背景 ​ 如果让我评选最伟大的数据结构&#xff0c;在我心中答案只有两个&#xff0c;数组和哈希表&#xff0c;这两个是我的程序的重要组成部分&#xff0c;同时也是我饭碗的重要组成部分。slice和map简洁明了的API很容易让我们有一种他们提供了无限大的空间&#xff0c;可以…...

Java实现单向链表

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;Java数据结构与算法 &#x1f9…...

3月4日,30秒知全网,精选7个热点

///印度最大供电商罕见于现货市场购煤&#xff0c;能源供应短缺成忧 据知情人士透露&#xff0c;这家印度国有发电公司计划在下周左右发布300万吨的招标 ///QQ音乐推出AIGC黑胶播放器 这是国内音乐行业首个运用AI技术&#xff0c;通过文字、图片指令快速生成不同风格的播放器…...

EXCEL-职业版本(2)

Excel-职业版本&#xff08;2&#xff09; 定位 1.如何快速定位到不连续的空值&#xff0c;填充为0 1.在任意空单元格里复制0 2.选中数据区域CtrlA 3.CtrlG 4.选择【定位条件】 5.选择【空值】 6.ctrlV 粘贴 即可 2.怎么一次性计算每个小组的数量 单价和金额的和? 1.选中…...

java中延时队列的实现

大家好&#xff0c;我是一名CRUD工程师&#xff0c;最近我朋友突然来问我如何实现延时队列&#xff0c;我脱口而出就是MQ。不过突然想到公司的项目好像用的是java的一个原生类。于是我就想着趁周末的时间好好的去探究一下各方法实现延时队列的优缺点。 延迟消息 延迟消息就是字…...

基于java的circle buffer的实现

总目录链接==>> AutoSAR入门和实战系列总目录 文章目录 缓冲区示例什么是循环缓冲区?方法 1:使用数组插入元素删除元素方法 2:使用链表插入元素:删除元素:当数据经常从一个地方移动到另一个地方或从一个进程移动到另一个进程或被频繁访问时,它不能存储在永久性内存…...