Effective STL条款21

记一次BUG: set / map 添加元素失败??? /* (strict weak ordering) */ 文章目录BUG 描述set / map 的 insert / emplace 的返回值set / map 的去重于是 BUG 就诞生了 :!!! BUG 描述 记录一个纠结了一天的 BUG: 向 set 容器中添加自定义类, 然后发现并没有添加进去, 真的是头大, 排查发现告诉我已经有相同 key 值元素存在了, 可我明明两个值不同 set / map 的 insert / emplace 的返... 阅读详情

条款21: 永远让比较函数对相等的值返回false

让我向你展示一些比较酷的东西。建立一个set,比较类型用less_equal,然后插入一个10:

set<int, less_equal<int> > s;    // s以“<=”排序
s.insert(10);      // 插入10

现在尝试再插入一次10:

s.insert(10);

对于这个insert的调用,set必须先要判断出10是否已经位于其中了。 我们知道它是,但set可是木头木脑的,它必须执行检查。为了便于弄明白发生了什么,我们将一开始已经在set中的10称为10A,而正试图插入的那个10叫10B。

set遍历它的内部数据结构以查找哪儿适合插入10B。最终,它总要检查10B是否与10A相同。关联容器对“相同”的定义是等价(equivalence)(参见条款19),因此set测试10B是否等价于10A。当执行这个测试时,它自然是使用set的比较函数。在这一例子里,是operator<=,因为我们指定set的比较函数为less_equal,而less_equal意思就是operator<=。于是,set将计算这个表达式是否为真:

!(10A <= 10B) && !(10B <= 10A)    // 测试10A和10B是否等价

哦,10A和10B都是10,因此,10A <= 10B 肯定为真。同样清楚的是,10B <= 10A。于是上述的表达式简化为

!(true) && !(true)

再简化就是

false && false

结果当然是false。 也就是说,set得出的结论是10A与10B不等价,因此不一样,于是它将10B插入容器中10A的旁边。在技术上而言,这个做法导致未定义的行为,但是通常的结果是set以拥有了两个为10的值的拷贝而告终,也就是说它不再是一个set了。通过使用less_equal作为我们的比较类型,我们破坏了容器!此外,任何对相等的值返回true的比较函数都会做同样的事情。根据定义,相等的值却不是等价的!是不是很酷?

OK,也许你对酷的定义和我不一样。就算这样,你仍然需要确保你用在关联容器上的比较函数总是对相等的值返回false。但是,你需要保持警惕。对这条规则的违反容易达到令人吃惊的后果。

举个例子,条款20描述了该如何写一个比较函数以使得容纳string*指针的容器根据string的值排序,而不是对指针的值排序。那个比较函数是按升序排序的,但我们现在假设你需要string*指针的容器的降序排序的比较函数。自然是抓现成的代码来修改了。如果你不细心,可能会这么干,我已经加亮了对条款20中代码作了改变的部分:

struct StringPtrGreater:    // 高亮显示
public binary_function<const string*,  // 这段代码和89页的改变
const string*,     // 当心,这代码是有瑕疵的!
bool> {
 bool operator()(const string *ps1, const string *ps2) const
 {
  return !(*ps1 < *ps2);   // 只是相反了旧的测试;
 }      // 这是不对的!
};

这里的想法是通过将比较函数内部结果取反来达到反序的结果。很不幸,取反“<”不会给你(你所期望的)“>”,它给你的是“>=”。而你现在知道,因为它将对相等的值返回true,对关联容器来说,它是一个无效的比较函数。

你真正需要的比较类型是这个:

struct StringPtrGreater:     // 对关联容器来说
public binary_function<const string*,   // 这是有效的比较类型
const string*,
bool> {
 bool operator()(const string *ps1, const string *ps2) const
 {
  return *ps2 < *ps1;   // 返回*ps2是否
 }      // 大于*ps1(也就是
};       // 交换操作数的顺序)

要避免掉入这个陷阱,你所要记住的就是比较函数的返回值表明的是在此函数定义的排序方式下,一个值是否大于另一个。相等的值绝不该一个大于另一个,所以比较函数总应该对相等的值返回false。

唉。

我知道你在想什么。你正在想,“当然,这对set和map很有意义,因为这些容器不能容纳复本。但是multiset和multimap怎么样呢?那些容器可以容纳复本,那些容器可能包含副本,因此,如果容器认为两个值相等的对象不等价,我需要注意些什么?它将会把两个都存储进去的,这正是multi系列容器的所要支持的事情。没有问题,对吧?”

错。想知道为什么,让我们返回头去看最初的例子,但这次使用的是一个mulitset:

multiset<int, less_equal<int> > s;   // s仍然以“<=”排序
s.insert(10);     // 插入10A
s.insert(10);     // 插入10B

现在,s里有两个10的拷贝,因此我们期望如果我们在它上面做一个equal_range,我们将会得到一对指出包含这两个拷贝的范围的迭代器。但那是不可能的。equal_range,虽然叫这个名字,但不是指示出相等的值的范围,而是等价的值的范围。在这个例子中,s的比较函数说10A和10B是不等价的,所以不可能让它们同时出现在equal_range所指示的范围内。

你明白了吗?除非你的比较函数总是为相等的值返回false,你将会打破所有的标准关联型容器,不管它们是否允许存储复本。

从技术上说,用于排序关联容器的比较函数必须在它们所比较的对象上定义一个“严格的弱序化(strict weak ordering)”。(传给sort等算法(参见条款31)的比较函数也有同样的限制)。如果你对严格的弱序化含义的细节感兴趣,可在很多全面的STL参考书中找到,比如Josuttis的《The C++ Standard Library》[3](译注:中译本《C++标准程序库》P176),Austern的《Generic Programming and the STL》(译注:中译本《泛型程序设计与STL》)[4],和SGI STL的网站[21]。 我从未发现这个细节如此重要,但一个对严格的弱序化的要求直接指向了这个条款。那个要求就是任何一个定义了严格的弱序化的函数都必须在传入相同的值的两个拷贝时返回false。

嗨! 这就是这个条款!

Effective C++ 第三版 所有条款总结及对应代码描述(纯干货) Effective C++ 第三版 所有条款总结及对应代码描述(纯干货) 阅读详情

相关推荐

robomimic实验复现全流程指南:从数据集下载到训练视频生成

本文提供了RoboMimic实验复现的完整实战指南,重点解析了从数据集下载、配置文件定制到模型训练与评估的全流程。以tool_hang任务为例,详细说明了环境准备、数据获取、训练监控及视频生成等关键步骤,并针对常见问题给出了排查方案,帮助研究者高效完成机器人模仿学习实验的闭环验证。

2w3e4r5t6y的博客 1033

一次stl sort调用导致的进程崩溃(zz)

http://blog.sina.com.cn/s/blog_532f6e8f01014c7y.html   近期我们开发的一个工具在调用c++ sort函数对数组进行排序时居然会导致进程崩溃,此问题细节我觉得对于类似我这种不常用stl的同学可能不容易觉察,这里简单总结下。 出错代码 因为代码太复杂不好展示,我这里就用下面这个简单的示例来描述。 sort调用导致的进程崩

梦想狂奔的专栏 1118

关于使用perfetto(systrace)对卡顿进行分析-实战分析

本文主要讲述了我在使用perfetto分析小米中桌面滑动卡顿的方法以及步骤。 这是我用的trace文件: 链接:https://pan.baidu.com/s/11EhCvu0pfR7Qfu6bDf_GLw?pwd=ic43 提取码:ic43Perfetto 是用于性能检测和跟踪分析的生产级开源堆栈。它提供用于记录系统级和应用级跟踪、本机 + java 堆分析、使用 SQL 分析跟踪的库以及用于可视化和浏览多 GB 跟踪的基于 Web 的 UI 的服务和库。在地址栏输入https://ui.perfe

qq_40494059的博客 1万+

《Effective C++》学习总结(条款21- 30)

条款21:必须返回对象时,别妄想返回其reference 1.在任何函数中返回一个reference(或指针)指向某个local对象,都没有任何意义——因为其即将被销毁 2.不要返回pointer或reference指向一个heap对象(用户不知道如何delete) 3.不要返回pointer或者reference指向local static对象而有可能需要多个这样的对象(同一行不能调用多次该函数,static只有一份) 4.当你必须在“返回一个reference”和“返回一个object”之间抉择,你需要

无限进步 889

SAP SD RRB实战:从DIP参数文件到DP90/DP91开票的完整链路解析

本文详细解析了SAP SD模块中RRB(Resource-related Billing)业务场景的完整链路,从DIP参数文件配置到DP90/DP91开票操作。通过实战案例分享DIP参数文件的核心配置技巧、特性设置及物料决定要点,帮助用户掌握动态开票的关键技术,提升SAP SD RRB的实施效率。

weixin_26833939的博客 378

别再用MounRiver了!在VSCode里用PlatformIO搞定CH32V208开发(附完整配置流程)

本文详细介绍了如何在VSCode中使用PlatformIO进行CH32V208开发,替代传统的MounRiver Studio。通过完整的配置流程和优化技巧,开发者可以构建高效、现代化的RISC-V开发环境,显著提升编译速度和开发效率。

weixin_28711397的博客 534

Effective C++ 条款20、21

条款20 宁以pass-by-reference-to-const替换pass-by-value条款21 必须返回对象时,别妄想返回其reference以上内容均来自Scott Meyers大师所著Effective C++ version3,如有错误地方,欢迎指正!相互学习,促进!!...

爱吃_宫爆鸡丁 370

基于YOLOv8和PaddlepaddleOCR的微信截图聊天记录提取

本文提出了一种基于YOLOv8和PaddleOCR的微信聊天记录提取方法。首先使用YOLOv8n模型分割出聊天气泡(针对深浅色模式分别训练),通过合成数据集增强模型鲁棒性,召回率达0.995。随后采用PP-OCRv5进行文本识别,利用坐标匹配将气泡内文本送入识别队列,短长文本平均置信度分别>0.98和>0.95。文中特别强调了PaddleOCR不同版本的输出格式差异及适配方案,建议使用3.0+版本。该方法有效解决了直接OCR识别易受干扰的问题,通过气泡分割+文本定位的两阶段策略显著提升了识别准确

2401_87383958的博客 2356

STL- strict weak ordering

今天运行程序突然崩溃, debug也无法定位到具体出错位置, 看了一眼崩溃信息expression invalid comparator, 顾名思义,可能是排序出错了。经过百度发现, std::sort的排序需要满足strict weak ordering, 即 cmp(a, b) != cmp(b, a) 经过阅读自定义的比较函数发现, 比较函数定义如下 bool cmp(a, b){ return a-b; } 如果a与b相等, 那么cmp(a, b)与cmp(b, a)的结果是一样的, 换句话说

michealoven的博客 316

LangGraph实战训练营-打造 WhatsApp 全自动消息收发AI智能助手

本文系统梳理了项目架构(WhatsApp Bridge + Talon Host + LangGraph Agent Harness三层设计)、完整环境搭建流程(Docker/本地双模式部署)、核心原理(WhatsApp桥接机制、NVIDIA Parakeet语音转录管线、Cron定时调度、Agent图状状态机),并提供了基础对话、文件操作、语音处理、定时任务等六大实战示例。同时涵盖安全策略、性能优化、数据备份等生产级运维实践,以及十个高频问题的排查方案。

program哲学 192

Windows 11 安装 JDK 25:下载、安装、配置环境变量、验证

本文详细介绍了在 Windows 11 系统上安装 JDK 25 的完整流程,包括从 Oracle 官网下载压缩包、解压至自定义目录、配置 JAVA_HOME 与 Path 环境变量,并验证安装是否成功。文章推荐使用 ZIP 压缩包而非 EXE 安装程序,以实现绿色便携、多版本共存和灵活管理。最后通过一个使用 Java 25 新特性的示例程序,验证 JDK 的编译与运行环境。

寒水馨 · 轻量级技术文档 · hanshuixin.org 7663

Effective STL学习笔记-条款21

永远让比较函数对相等值返回false举一个set的例子: set<int, less_equal<int>> Set 我们多次插入同一个值,测试。 Set.insert(10); Set.insert(10);实际上在我的vs2015编辑器上,运行当第二个insert触发是,就会抛出一个异常,提示一个无效的比较器。这是因为set进行插入值时,会使用这个比较器进行对比,而将 10 <

柠檬柠檬C的博客 862

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Effective STL条款21 (转)

Effective STL条款21 (转)[@more@]条款21: 永远让比较函数对相等的值返回false 让我向你展示一些比较酷的东西。建立一个set,比较类型用less_equal,然后插入一个10: set >...

csd3176的博客 99

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Effective STL 条款21: 关联容器的strict weak order(让比较函数对相等的值返回false)

条款21: 永远让比较函数对相等的值返回false 让我向你展示一些比较酷的东西。建立一个set,比较类型用less_equal,然后插入一个10: set > s; // s以“<=”排序 s.insert(10); // 插入10 现在尝试再插入一次10: s.insert(10); 对于这个insert的调用,set必须先要判断出10是否已经位于其中了

wkyb608的专栏 603

Effective STL 条款21: 关联容器的strict weak order(让比较函数对相等的值返回false) .

条款21: 永远让比较函数对相等的值返回false 让我向你展示一些比较酷的东西。建立一个set,比较类型用less_equal,然后插入一个10: set > s; // s以“<=”排序 s.insert(10); // 插入10 现在尝试再插入一次10: s.insert(10); 对于这个insert的调用,set必须先要判断出10是否已经位于其中了。 我们

C++ 爱好者 make_it_simple888 1337

Effective STL 条款21: 关联容器的strict weak order(让比较函数对相等的值返回false) ....

条款21: 永远让比较函数对相等的值返回false 让我向你展示一些比较酷的东西。建立一个set,比较类型用less_equal,然后插入一个10: set&lt;int, less_equal&lt;int&gt; &gt; s; // s以“&lt;=”排序 s.insert(10); // 插入10 现在尝试再插入一次10: s.insert(10); 对于这...

java开发指南博客 【转载】 95

C++使用sort排序导致的coredump(Strict Weak Ordering)

当我们需要自定义排序规则时,需要实现一个比较函数,该函数类似如下:bool cmp(int a,int b) { return a>b; }当cmp返回true时,a将会排在b前面,因此上面的函数将从大到小排序。 换句话说,cmp函数重新定义了“小”的概念(当a>b时,a“小于”b),整个序列将按照这个“小”的规则从“小”到“大”排序。前几天同事遇到一个问题:对一组数据从小到大排序,对于值相等的

jiange_zh的博客 4642

Effective STL学习笔记-条款31

了解你的排序选择个人认为掌握并灵活的使用stl的算法,对我们c++编程有着事半功倍的效果,排序算法的使用和掌握更加重要。使用的最多的算法可能就是sort了,或者qsort(条款46的存在可能会让更多人倾向于使用sort而不是qsort)。例如有一个Widget的Vector,你需要挑选出Widget质量最好5个,剩下的可以保持无序。 这时候我们不需要完全排序,sort显得有些浪费。 而parta

柠檬柠檬C的博客 418

《Effective C++》资源管理:条款20-条款21

这两个条款讲述了何时应该使用引用传递pass by reference,何时应该使用值传递pass by value。

KangRoger的专栏 2506

Effective STL 条款35

条款35:通过mismatch或lexicographical比较实现简单的大小写无关字符串比较一个STL菜鸟最常问的问题是“我怎么使用STL来进行大小写无关的字符串比较?”这是一个令人迷惑的简单问题。大小写无关字符串比较要么真的简单要么真的困难,依赖于你要多一般地解决这个问题。如果你忽略国际化问题而且只关注于设计成字符串strcmp那样的类型,这个任务很简单。如果你要有strcmp不具有

AMin2001的专栏 1859

Effective STL 条款3

条款3:使容器里对象的拷贝操作轻量而正确 容器容纳了对象,但不是你给它们的那个对象。此外,当你从容器中获取一个对象时,你所得到的对象不是容器里的那个对象。取而代之的是,当你向容器中添加一个对象(比如通过insert或push_back等),进入容器的是你指定的对象的拷贝。当你从容器中获取一个对象时(比如通过front或back),你取到的是容器中那个对象的拷贝。拷进去,拷出来。这就是STL的方

AMin2001的专栏 1680
上一篇: Effective STL条款44
下一篇: Effective STL 条款45
AMin2001
博客等级 码龄26年 7粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值