我们知道,对于map的键类型,唯一的约束是必须支持<操作符,至于是否支持其他的关系或相等运算,则不做要求
如果map的Key为内置类型,则通常已支持<操作,比如map<int>,int类型已经支持<
如果是自定义类型呢?则必须重载<!
class MyStruct
{
public:
int x;
int y;
int z;
MyStruct(int ix=0,int iy=0,int iz=0)
{
x = ix;
y = iy;
z = iz;
}
};
map<MyStruct,string> mm;
mm.insert(make_pair(MyStruct(1,1,1),"1") );
如果没有重载<,编译的时候会报错:
d:\program files\microsoft visual studio 9.0\vc\include\functional(143) : error C2784: “bool std::operator <(const std::_Tree<_Traits> &,const std::_Tree<_Traits> &)”: 无法从“const MyStruct”为“const std::_Tree<_Traits> &”推导 模板 参数
1> d:\program files\microsoft visual studio 9.0\vc\include\xtree(1466) : 参见“std::operator <”的声明
于是这里重载下<
friend bool operator< (const MyStruct &ms1,const MyStruct &ms)
{
return ms1.x < ms.x && ms1.y < ms.y && ms1.z < ms.z;
}
mm.insert(make_pair(MyStruct(1,1,1),"1") );
mm.insert(make_pair(MyStruct(1,2,1),"2"));
mm.insert(make_pair(MyStruct(1,1,0),"3"));
经过调试代码,我们发现,实际只插入成功了第1条:
这是为什么呢?
------------------------------------华丽丽的分割线-------------------------------------------
这还得从map的机制说起,比如上面有调用3次map的insert接口
第一次:map为空,插入成功
第二次/第三次:查找key是否存在,如果已存在,则插入失败
所以看来问题应该出在查找Key上面
从Key类型的定义来看,我们只重载了<,那在做insert操作时,如何判断Key是否存在?
前面我们已经说过,Key只定义了<号的约束,是否重载==,并不关注,所以这里不能通过重载==判断Key是否存在
其实,通过<也能判断一个值是否相等
比如a=1,b=2,当!(a<b) && !(b<a)时,a和b相等,这里!(1<2)为fasle,所以1!=2
if a=1,b=1 --> !(1<1) && !(1<1) 为真,则可认为两个数相等
其实从这里可以看出一个重载map Key类型<号的一个基本原则,即当a<b为真时,b<a则必定为假,反正亦然,这样才能保证正确的通过<判断出==
再看下我们之前重载的<
return ms1.x < ms.x && ms1.y < ms.y && ms1.z < ms.z;
1、已存在(1,1,1),再计算插入(1,2,1)
上面我们说了,基本原则为:a<b为和b<a的值要求不一致,但是这里明显不符合:
第一次调用时首先判断1<1,为假,operator<会直接返回fasle
第二次调用时,按照我们的原则,如果返回true,则重载<是正确的,但很不幸,还是比较1<1,依旧为false
显然,这里重载<是错误的
那如何才能重载出正确的<呢?
这里给一个正确的例子:
friend bool operator< (const MyStruct &ms1,const MyStruct &ms)
{
if (ms1.x != ms.x)
{
return ms1.x < ms.x;
}
if (ms1.y != ms.y)
{
return ms1.y < ms.y;
}
if (ms1.z != ms.z)
{
return ms1.z < ms.z;
}
return false;
}
这里当判断a<b时,会返回false
b<a时,同样也会返回false
执行结果: