Effective STL: Item 21:永远让比较函数对相同元素返回false

原创 2003年03月09日 23:48:00

 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

Item 21:永远让比较函数对相同元素返回false

 

让我展示些比较酷的东西。构建一个set,比较类型用的是less_equal,然后insert一个10:

set<int, less_equal<int> > s;               // s is sorted by <=

s.insert(10);                               // insert the value 10

现在尝试再insert一次10:

s.insert(10);

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

set遍历它的内部数据结构以查找加入10B的位置。 最终,它总要检查10B是否与10A相同。 关联容器对“相同”的定义是equivalence(见Item 19)(WQ注: equivalence应指“数学相等”,two elements are equal if neither is less than the other,见《The C++ Standard Library》中文版P82,英文版电子P77;equality指“逻辑等价”,使用operator==(),见《The Standard Template Library》英文电子版P30)因此set测试10B是否数学等值于10A。 当执行这个测试时,它自然是使用set的比较函数。在这一例子里,是operator<=,因为我们指定set的比较函数为less_equal,而less_equal就是operator<=。于是,set将计算这个表达式是否为真:

!(10 A <= 10 B ) && !(10 B <= 10 A )       // test 10 A and 10 B for equivalence

好吧10A10 B都是10,因此10A <= 10B 肯定为真。同样10A <= 10B。上述的表达式简化为

!(true) && !(true)

再简化就是

false && false

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

好吧,也许你对酷的定义和我不一样。就算这样,你仍然需要确保你用在关联容器上的比较函数总是对相同的元素返回false。然而,你需要警惕。对这条规则的违反容易到令人吃惊的程度。

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

struct StringPtrGreater:                             // highlights show how

public binary_function<const string*,            // this code was changed

                           const string*,            // from page 89. Beware,

                       bool> {                   // this code is flawed!

bool operator()(const string *ps1, const string *ps2) const

{

return !( *ps1 < *ps2);                      // just negate the old test;

}                                                // this is incorrect!

};

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

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

struct StringPtrGreater:                             // this is a valid

public binary_function<const string*,            // comparison type for

                           const string*,            // associative containers

                           bool> {

bool operator()(const string *ps1, const string *ps2) const

{

return *ps2 < *ps1;                          // return whether *ps2

}                                                // precedes *ps1 (i.e., swap

                                                     // the order of the

};                                                   // operands)

要避免掉入这个陷阱,你所要记住的就是比较函数的返回值指明的是在此函数定义的排序方式下,一个元素是否应该位于另一个之前。相同的元素绝不该一个领先于另一个,所以比较函数总应该为相同的元素返回false。

唉。

我知道你在想什么。你正在想,“当然,这对set和map很有意义,因为这些容器不能容纳复本。但是multiset和multimap怎么样呢?那些容器可以容纳复本,那些容器可能包含副本,因此,如果容器认为两个相同元素对象不等值,我需要注意些什么(so what do I care if the container thinks that two objects of equal value arent equivalent?)?它将会把两个都存储进去的,这正是multi系列容器的所要支持的事情。没有问题存在,对吧?”

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

multiset<int, less_equal<int> > s;             // s is still sorted by <=

s.insert(10);                                  // insert 10 A

s.insert(10);                                  // insert 10 B

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

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

从技术上讲,用于关联容器的比较函数必须在它们所比较的对象上定义一个“strict weak ordering”。 ( 传给sort(Item 31)等泛型算法的比较函数也有同样的限制)。如果你对strict weak ordering的含义细节感兴趣,可在很多STL指导书籍中找到,比如Josuttis的《The C++ Standard Library》(WQ注:中文版P176,英文版电子P156),Austern的《Generic Programming and the STL 》,和SGI STL的网站。 我从未发现这个细节如此重要但一个对strict weak ordering的要求直接瞄上了这个Item 那个要求就是任何定义了一个strict weak ordering 的函数都必须在传入相同元素的两个拷贝时返回false

嗨! 那是这个Item!

 

 

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

#include #include #include #include #include #includeusing namespace std;int main() { set s;//s采...
  • u014110320
  • u014110320
  • 2016年09月22日 20:12
  • 316

C++中关于set的自定义排序函数的书写

大概有两个月没用过C++啦,手都变得很生了,在这里,在这里我想扯一下关于set的比较函数的定义,我想,应该有不少人对这个东西感到头疼吧!如果说我想在set里面添加一个自定义的类型,比如说下面的结构体:...
  • lishuhuakai
  • lishuhuakai
  • 2016年05月14日 09:07
  • 6862

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

一、问题引入 让我向你展示一些比较酷的东西。 建立一个set,比较类型用less_equal,然后插入一个10: // s以“ set > s;    s.insert(10);  ...
  • wangdamingll
  • wangdamingll
  • 2016年12月13日 10:12
  • 194

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

 条款21: 永远让比较函数对相等的值返回false让我向你展示一些比较酷的东西。建立一个set,比较类型用less_equal,然后插入一个10:set > s; // s以“...
  • bichenggui
  • bichenggui
  • 2009年10月20日 21:38
  • 5444

重构的小技巧

1. CSS3对话框 .talk{width: 100px; height: 30px; background: #ffffff; border-radius: 5px; position: ab...
  • hao452378531
  • hao452378531
  • 2016年05月10日 10:06
  • 696

深入理解Android之Gradle

深入理解Android之GradleGradle是当前非常“劲爆”得构建工具。本篇文章就是专为讲解Gradle而来。介绍Gradle之前,先说点题外话。一、题外话说实话,我在大法工作的时候,就见过Gr...
  • Innost
  • Innost
  • 2015年09月05日 20:07
  • 130505

火眼睛睛查coredump(stl sort)------永远让比较函数对相同元素返回false

转载:http://blog.csdn.net/stpeace/article/details/51040218#cpp 看看如下代码的一个非常隐晦的错误, 虽然不会每次core dump, 但...
  • zgaoq
  • zgaoq
  • 2017年07月08日 16:33
  • 274

火眼睛睛查coredump(stl sort)------永远让比较函数对相同元素返回false

看看如下代码的一个非常隐晦的错误, 虽然不会每次core dump, 但类似代码迟早会core dump, 好多人遇到过。 此问题极难定位, 看一下吧: #include #include #in...
  • stpeace
  • stpeace
  • 2016年04月02日 00:19
  • 3124

oracle分析函数

一、Oracle分析函数简介: 在日常的生产环境中,我们接触得比较多的是OLTP系统(即Online Transaction Process),这些系统的特点是具备实时要求,或者至少说对响应的时...
  • caohongshuang
  • caohongshuang
  • 2017年08月31日 21:01
  • 127

深入理解Android:Gradle详解

转帖《深入理解Android》系列图书作者邓凡平,大家互相学习,共同进步! Gradle是当前非常“劲爆”的构建工具。本篇文章就是专为讲解Gradle而来。介绍Gradle之前,先说点题外话。 一...
  • wushaominkk
  • wushaominkk
  • 2018年01月29日 10:30
  • 10
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Effective STL: Item 21:永远让比较函数对相同元素返回false
举报原因:
原因补充:

(最多只允许输入30个字)