前言
</br>
在STL中,对两个对象进行比较,并且判定它们是否具有相同的值,这种操作十分常见。举例而言,find算法可以定位区间中第一个具有某个特定值的元素,这种操作必须基于对象的比较。set的insert成员函数在执行插入前也必须明确该值是否已经存在于容器内部,这同样基于比较。
相等与等价
</br>
上文所说的find算法与insert成员函数都需要判定两个值是否相同,但其实现则完全不同。
find对“相同”的定义是相等,基于operator==。
set::insert对“相同”的定义是等价,通常基于operator<。
相等
相等的概念基于operator==
。如果表达式“x == y”返回true,我们认为x与y具有相等的值。这种相等是定义性的,只和operator==
的返回值有关。也就是说,只要operator==
撰写者愿意,我们完全可以判定猫和狗相等。
等价
等价基于一个有序区间中对象值的相对位置。也就是说,等价仅在关联容器内部排序时存在意义。等价的定义是,如果对象x与对象y在关联容器c中的排序顺序并不分先后,则它们等价。从逻辑角度而言,基于operator<
的等价有这样的形式:1
!(x<y) && !(y<x)
一般情况下关联容器的比较函数不是operator<
或 less
,是用户自定义的判断式。每个关联容器通过key_comp成员函数来访问排序判断式,因此真正意义上的等价有形式如下:1
!c.key_comp()(x, y) && !c.key_comp()(y, x) //key_comp返回一个函数(或函数对象)
问题实例
建立case-insensitive string set
假设需要建立一个忽略大小写的set<string>,显然,我们需要自行建立一个比较函数,该函数比较string对象的同时忽略大小写。set在声明时需要的是比较函数的类型,并非真的函数,因此我们构建了一个仿函数类(仿函数详见Effective STL·仿函数篇),并在operator()调用了忽略大小写的函数:1
2
3
4
5
6struct CIStringCompare: public binary_function<string, string, bool> {
//binary_function 详见Effective STL 40
bool operator()(const string& lhs,const string& rhs) const{
return ciStringCompare(lhs, rhs);//具体实现见Effective STL 35
}
}
该set的建立和使用如下:1
2
3set<string, CIStringCompare> ciss;//"case-insensitive string set"
ciss.insert("ABC"); // 一个新元素添加到set中
ciss.insert("abc"); // 没有新元素添加到set中
find成员函数与find算法的不同
我们在新建立的容器中查找刚才加入的”abc”(大小写被忽略),find成员函数与find算法返回的值却并不相同:1
2if (ciss.find("abc") != ciss.end())... //true
if (find(ciss.begin(), ciss.end(),"abc") != ciss.end())... //false
这是十分自然的,因为find成员函数判定”abc”等价于”ABC”,而find算法判定二者并不相等。
关联容器为什么需要基于等价来判定相同?
</br>
标准关联容器默认有序,所以STL关联容器必须存在一个保证有序的比较函数less。如果以相等来决定二者是否具有相同的值,则关联容器除了比较器之外还需要定义一个判断相等的比较函数。这两个函数可能会引发冲突。
举例而言,假定存在一个类似set的STL容器叫做set2CF(set with two comparison functions)。第一个比较函负责定set内部元素的排序,第二个用来判定两个元素是否相同。1
set2CF<string, CIStringCompare, equal_to<string> > s;
排序函数判定字符串时不考虑大小写,而等价函数则认为,两个字符串对应字符完全相同时它们相同。(此时存在两个比较函数的逻辑混乱)当执行如下操作时:1
2s.insert("ABC");
s.insert("abc");
从第一比较器的角度而言,二者是等价的,第二个不会被插入。第二比较器则认为二者并不相同。
那如果遵循第二比较器,把两个string都放入set,那么它们又应该处于什么顺序呢?如果随意的插入,则等于放弃了顺序遍历set的能力。(set内无法保证有序)
综上,在关联容器中使用等价作为相同判定时具备先天优势。(关联容器必须有序,而等价的定义就和顺序相关)