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

记一次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。

http://blog.youkuaiyun.com/bichenggui/article/details/4705472

ArduSub程序学习(1)——ardupilot框架整理 AP_HAL层(硬件抽象层)是我们制作 ArduPilot 的方式可移植到许多不同的平台。有一个顶级AP_HAL libraries/AP_HAL,用于定义代码其余部分的接口 有针对具体的板子特性,然后就有了AP_HAL_XXX 子目录,例如,AP_HAL_AVR 用于基于 AVR 的 开发板,AP_HAL_PX4用于 Pixhawk 开发板和 AP_HAL_Linux 基于 Linux 开发板。工具目录tools directories工具目录是杂项支持目录。 阅读详情

相关推荐

前导0预测(Leading-Zero Anticipator,LZA)

前导0预测(Leading-Zero Anticipator,LZA) 浮点加法在完成之后会进行尾数规格化移位,而在进行尾数加法时,可以使用前导0预测(Leading-Zero Anticipator,LZA)同时计算移位,前导0预测只发生在一个正数加上一个负数的情况下,电路图如图1所示。 图1 传统LZD算法和LZA算法 1. 基础算法 算法的目标是计算出加法结果序列左边连续0的数量,下文中对...

crw1111111的博客 7379

Effective STL条款21

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

AMin2001的专栏 1310

深度学习CV八股文

深度学习CV八股文一、深度学习中解决过拟合方法L1和L2正则化DropoutDropout正则化Inverted Dropout(反向随机失活)Dropout起到正则化效果的原因:Dropout的缺点eargstopping ( 早停法 )二、深度学习中解决欠拟合方法三、梯度消失和梯度爆炸解决梯度消失的方法解决梯度爆炸的方法四、神经网络权重初始化方法Xavier指数加权移动平均数五、梯度下降法梯度下降SGD(随机梯度下降法)Momentum(动量梯度下降)Nesterov Momentum自适应学习率算法A

填坑 2万+

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

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

梦想狂奔的专栏 1118

类银河恶魔城游戏开发学习笔记(0)

解决B站搬运【Unity教程】从0编程制作类银河恶魔城游戏P37中moveState和idleState循环切换的问题

Gellar的博客 1448

6.4 构网型储能的需求与配置

摘要: 构网型储能是新型电力系统的关键支撑技术,通过提供电压/频率形成、惯性和稳定控制能力,解决同步机退出导致的系统稳定性问题。其配置需遵循三大原则:1)容量配置以支撑需求为主导,兼顾能量服务;2)优先布局于电网薄弱点、稳定问题突出区域;3)采用自适应控制策略实现全工况优化。核心应用场景包括新能源送出通道稳定、负荷中心调频、黑启动电源等。规划方法需结合系统分析、量化指标和成本效益评估,实现技术经济最优。未来需重点关注构网/跟网混合系统的临界容量占比及自适应控制技术发展。

技术工作者 1140

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

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

无限进步 889

踩坑2周总结:Python+YOLO26搭建工业级多路视频分析应用,从单路卡顿到16路4K稳跑30FPS

本文分享了工业级多路视频分析系统的实战经验,针对智慧物流园区的车辆违停和人员安全监测需求,提出了一套完整的解决方案。作者通过踩坑实践,总结出多路视频分析的四大核心痛点:视频流解码瓶颈、推理调度混乱、内存占用爆炸和部署兼容性差。 文章详细介绍了技术选型方案,推荐使用FFmpeg+PyAV进行硬件加速解码、YOLO26+TensorRT INT8量化推理、单进程多线程的生产者-消费者架构,以及内存池复用等技术组合。该方案在RTX 4070 Ti上实现了16路4K视频30FPS稳定运行,GPU利用率达85%以上,

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 955

Effective C++ 条款20、21

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

爱吃_宫爆鸡丁 370

全面解读AI Infra:架构、生态与未来趋势

文章摘要 大模型时代下,AI基础设施(AI Infra)成为关键技术基建,涵盖从数据准备到模型服务的全栈技术体系。文章系统解构了AI Infra的四层架构(硬件资源层、资源管理调度层、开发框架层、平台工具层),并分析了全球AI Infra生态图谱,包括云计算巨头、芯片厂商等全栈服务商,以及中国特色的多元竞合格局。通过对比Data Infra与AI Infra的核心差异,指出AI Infra更关注智能应用的规模化与自动化。文章还探讨了从传统数据设施向AI基础设施的转型路径,强调自主可控、软硬协同等发展趋势,为

技术引领业务创新 610

NVIDIA Isaac GR00T N1.5 人形机器人强化学习入门教程(四)Lerobot、宇树 G1 等不同形态机器人微调教程

本笔记本是一份关于如何在新的数据集上对GR00T-N1预训练模型进行微调的教程。

2506_90492529的博客 1667

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是否已经位于其中了。

David的专栏 6390

UNET改进77:全网首发-添加CFBlock |

本文介绍了在UNet模型中集成CFBlock注意力机制的实现方法。主要分为三个步骤:1) 创建CFBlock模块,包含卷积注意力机制和MLP;2) 在UNet连接处添加注意力模块;3) 在卷积层中嵌入注意力机制。具体实现包括:定义Conv、MLP和ConvolutionalAttention等基础模块,构建CFBlock核心结构;在UNet跳跃连接处插入注意力模块;将注意力机制整合到DoubleConv和Down模块中。该方法通过在不同位置引入注意力机制,可有效提升模型的特征提取能力。代码实现使用PyTor

AIcurator的博客 388

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

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

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

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

effective stl 第21条:总是让比较函数在等值情况下返回false

#include<iostream> #include<set> #include<map> #include<algorithm> #include<functional> #include<iterator>using namespace std;int main() { set<int, less_equal<int>> s;//s采用“<=”来排序 s.insert(10);

sun%moon 752

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Effective STL学习笔记-条款21

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

柠檬柠檬C的博客 862

Effective STL: Item 21: Always have comparison functions return

Let me show you something kind of cool. Create a set where less_equalis the comparison type, then insert 10 into the set:set > s; // s is sorted by “s.insert(10); // insert the value 10Now try inserti

copy的专栏 961

关联容器:永远让比较函数对相等的值返回false

一、问题引入 让我向你展示一些比较酷的东西。 建立一个set,比较类型用less_equal,然后插入一个10: // s以“ set > s;    s.insert(10);    现在尝试再插入一次10: s.insert(10)   对于这个insert的调用,set必须先要判断出10是否已经位于其中了。 我们知道它是,但set可是木头木脑的,它必须执行检查

wangdamingll的博客 798

Effective_STL 学习笔记(二十一) 永远让比较函数对相等的值返回 false

除非比较函数总是为相等的值返回 false,否则将会打破所有的标准关联容器, 不管关联容器是否允许存在副本(set、map、multiset、multimap) 对于(set、map)使用 less_equal (<=): 1   !( 10A <= 10B ) && !( 10B <= 10A );  // 测试10A 和10B是否相等 ...

weixin_33862041的博客 196

C++ sort函数的使用注意事项及潜在bug

严格弱序的定义:如果两个元素具备任何一个元素都不小于另一个元素的性质,那么就视为它们是具备某种程度之等价关系是合理的。(x<y) &&!(y<x) , 广义上!op(x,y) &&!op(y,x) (op(x,y) 表示比较x和y)而不能是 <= 或者 >= ,(实际上等于号加了也是毫无意义,sort是不稳定的排序),否则可能会出现段错误。sort函数的cmp必须按照规定来写,即必须只是 > 或者 <比如: return a > b;《c++标准程序库》 P176。《泛型编程与STL》第六章。

bkhyv的博客 957

Effective STL 21~25 (传值 class成员变量 non-menber函数)

条款21:必须要返回对象时,别妄想返回其reference 尽量不要在返回pointer 或 reference 指向一个local stack对象。 以后写法尽量按上述的去写。不然对其消耗太大。 条款22:将成员变量声明为private 切记将成员变量声明为private。这可以赋予客户访问数据的一致性,可细微分划分访问控制、提供了class作者以充分的实现弹性。 protected...

比卡丘不皮的博客 315

21. 总是让比较函数在等值情况下返回false

有例子如下: std::set<int, std::less_equal<int>> datas; datas.insert(10); // 10a datas.insert(10); // 10b 称第一次插入的10为10a,第二次插入的为10b。按照设想,10b不能插入到datas中,因为10b的值也是10。结果却是能够插入。 注意,关联容器“相同”的定义是等价,10...

xupeng1644的博客 1739
上一篇: 函数内临时变量的释放时刻
下一篇: TOP K算法(微软笔试题 统计英文电子书中出现次数最多的k个单词)
C++程序员Carea
博客等级 码龄18年 1590粉丝 878原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值