# Effective STL: Item 21: Always have comparison functions return

Let me show you something kind of cool. Create a set where less_equal
is the comparison type, then insert 10 into the set:
set<int, less_equal<int> > s; // s is sorted by “<=”
s.insert(10); // insert the value 10
Now try inserting 10 again:
s.insert(10);
For this call to insert, the set has to figure out whether 10 is already
present. We know that it is, but the set is dumb as toast, so it has to
check. To make it easier to understand what happens when the set
does this, we’ll call the 10 that was initially inserted 10 A and the 10
that we’re trying to insert 10 B .
The set runs through its internal data structures looking for the place
to insert 10 B . It ultimately has to check 10 B to see if it’s the same as
10 A . The definition of “the same” for associative containers is equiva-lence
(see Item 19), so the set tests to see whether 10 B is equivalent to
10 A . When performing this test, it naturally uses the set’s comparison
function. In this example, that’s operator<=, because we specified
less_equal as the set’s comparison function, and less_equal means oper-ator<=.
The set thus checks to see whether this expression is true:
!(10 A <= 10 B ) && !(10 B <= 10 A ) // test 10 A and 10 B for equivalence
Well, 10 A and 10 B are both 10, so it’s clearly true that 10 A <= 10 B .
Equally clearly, 10 B <= 10 A . The above expression thus simplifies to
!(true) && !(true)
and that simplifies to
false && false
which is simply false. That is, the set concludes that 10 A and 10 B are
not equivalent, hence not the same,and itthus goes aboutinserting
10 B into the container alongside 10 A . Technically, this action yields
undefined behavior, but the nearly universal outcome is that the set
ends up with two copies of the value 10, and that means it’s not a set
any longer. By using less_equal as our comparison type, we’ve cor-rupted
the container! Furthermore, any comparison function whereഊequal values return true will do the same thing. Equal values are, by
definition, not equivalent! Isn’t that cool?
Okay, maybe your definition of cool isn’t the same as mine. Even so,
you’ll still want to make sure that the comparison functions you use
for associative containers always return false for equal values. You’ll
need to be vigilant, however. It’s surprisingly easy to run afoul of this
constraint.
For example, Item 20 describes how to write a comparison function
for containers of string* pointers such that the container sorts its con-tents
by the values of the strings insteadofthe valuesofthepointers.
That comparison function sorts them in ascending order, but let’s
suppose you’re in need of a comparison function for a container of
string* pointers that sorts in descending order. The natural thing to do
is to grab the existing code and modify it. If you’re not careful, you
might come up with this, where I’ve highlighted the changes to the
code in 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!
};
The idea here is to reverse the sort order by negating the test inside
the comparison function. Unfortunately, negating “<” doesn’t give you
“>” (which is what you want), it gives you “>=”. And you now under-stand
that “>=”, because it will return true for equal values, is an
invalid comparison function for associative containers.
The comparison type you really want is this one:
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)ഊTo avoid falling into this trap, all you need to remember is that the
return value of a comparison function indicates whether one value
precedes another in the sort order defined by that function. Equal val-ues
never precede one another, so comparison functions should
always return false for equal values.
Sigh.
I know what you’re thinking. You’re thinking, “Sure, that makes sense
for set and map, because those containers can’t hold duplicates. But
what about multiset and multimap? Those containers may contain
duplicates, so what do I care if the container thinks that two objects of
equal value aren’t equivalent? It will store them both, which is what
the multi containers are supposed to do. No problem, right?”
Wrong. To see why, let’s go back to the original example, but this time
we’ll use a multiset:
multiset<int, less_equal<int> > s; // s is still sorted by “<=”
s.insert(10); // insert 10 A
s.insert(10); // insert 10 B
s now has two copies of 10 in it, so we’d expect that if we do an
equal_range on it, we’ll get back a pair of iterators that define a range
containing both copies. But that’s not possible. equal_range,itsname
notwithstanding, doesn’t identify a range of equal values, it identifies
a rangeofequivalent values. In this example, s’s comparison function
says that 10 A and 10 B are not equivalent, so there’s no way that both
can be in the range identified by equal_range.
You see? Unless your comparison functions always return false for
equal values, you break all standard associative containers, regard-less
of whether they are allowed to store duplicates.
Technically speaking, comparison functions used to sort associative
containers must define a “strict weak ordering” over the objects they
compare. (Comparison functions passed to algorithms like sort (see
Item 31) are similarly constrained.) If you’re interested in the details of
what it means to be a strict weak ordering, you can find them in many
comprehensive STL references, including Josuttis’ The C++ Standard
Library [3], Austern’s Generic Programming and the STL [4], and the
SGI STL Web Site [21]. I’ve never found the details terribly illuminat-ing,
but one of the requirements of a strict weak ordering bears
directly on this Item. That requirement is that any function defining a
strict weak ordering must return false if it’s passed two copies of the
same value.
Hey! That is this Item!
• 本文已收录于以下专栏：

## Effective STL 21 Always have comparison functions return false for equal values

always have comparison functions return false for equal values
• ruihuank
• 2017年08月28日 12:00
• 62

2014-02-27 wcdj Address warnings The GNU compiler collection (GCC) 4.6.3 warns about suspicio...
• delphiwcdj
• 2014年02月27日 10:42
• 3404

## Effective STL 中文版(完整版)

Winter总算找到《Effective STL》的完整中文版了，奉献给大家。书中作者解释了怎样结合STL组件来在库的设计得到最大的好处。这样的信息允许你对简单、直接的问题开发简单、直接的解决方案，...
• WinterTree
• 2005年01月16日 01:23
• 16313

## GCC(ARM) warning: comparison is always true due to limited range of data type

char ch; while((ch = getopt(argc,argv,"d:h"))!=EOF switch(ch{ case 'h' ... GCC X86    ...
• u013122984
• 2013年12月19日 11:28
• 597

## effective C++ 读书笔记 条款21

effective C++ 读书笔记 条款21
• djb100316878
• 2014年11月06日 16:22
• 892

## Effective STL目录

从去年12月开始，我花了40天看了《Effective STL》这本书，并写了相关的读书笔记，我在考虑是否将这部分笔记在BLOG上贴出来。在此之前，先贴上Effective STL目录，并且强...
• hanyu1980
• 2006年06月20日 15:22
• 1166

## Effective STL 中文版(完整版）

Winter总算找到《Effective STL》的完整中文版了，奉献给大家。书中作者解释了怎样结合STL组件来在库的设计得到最大的好处。这样的信息允许你对简单、直接的问题开发简单、直接的解决方案，也...
• sdnxiaotao
• 2008年07月05日 13:23
• 438

## Effective STL学习

effect stl读书笔记
• skyztttt
• 2017年02月23日 17:11
• 376

## effective stl 第19条：理解相等（equality）和等价（equivalence）的区别

#include #include #includeusing namespace std;bool ciStringCompare(const string l, const string r) {...
• u014110320
• 2016年09月20日 23:36
• 251

## Item 31：最小化文件之间的编译依赖 Effective C++笔记

Item 31: Minimize compilation dependencies between files. 曾听老师讲过，每天上班的第一件事就是下载最新代码开始编译，然后可以有半个小...
• yangjvn
• 2015年09月19日 11:56
• 860

举报原因： 您举报文章：Effective STL: Item 21: Always have comparison functions return 色情 政治 抄袭 广告 招聘 骂人 其他 (最多只允许输入30个字)