当前位置:首页 > c++ > 正文内容

std::map自定义key,非严格弱序导致"invalid comparator"异常

xuwenyan9个月前 (07-14)c++669

std::map自定义key的方法是重写operator<(),但是如果没有严格弱序,极有可能导致”invalid comparator”异常,也就是提示比较器无效,如下demo代码:

class MyKey {
public:
  MyKey(int num1, int num2)
    : num1_(num1)
    , num2_(num2) {
  }

  bool operator<(const MyKey& key) const {
    return num1_ < key.num1_ ? true : num2_ < key.num2_;
  }

  int num1_;
  int num2_;
};

int main() {
  std::map<MyKey, int> map1;
  map1[MyKey(1, 2)] = 12;
  map1[MyKey(2, 1)] = 21;

  system("pause");
  return 0;
}

 

要搞清楚为什么会出现这个异常并解决,要先了解一下什么是严格弱序和operator<()是如何被std::map使用的。

什么是严格弱序?

严格弱序有三个特性:

  • 非对称性:两个关键字不能同时“严格弱序”于对方

  • 传递性:如果a“严格弱序”于b,且b“严格弱序”于c,则a必须“严格弱序”于c

  • 非自反性:如果存在两个关键字,任何一个都不“严格弱序”于另一个,则这两个关键字是相等的

通常我们认为”<”就是严格弱序,而”<=”是非严格弱序的,基于这个来看,把条件中的”严格弱序”替换成”<”,我们会发现严格弱序的三个特性是必然的,而反过来验证”<=”则是不成立的。

比较表示为:

  • a 小于 ba < b

  • a 大于 bb < a

  • a 等于 b!(a < b) && !(b < a)

operator<()如何被std::map使用的?

假设已经有一个为key1的元素,当我们需要插入一个key2的元素时:

通过单步调试代码的方式可以发现,operator<()被调用了两次,第一次是key1.operator<(key2),第二次是key2..operator<(key1)

那么问题来了,std::map的key只需要”<”比较,它是如何比较两个key相等的?这大概就是operator<()需要被调用两次的一个原因,当key1<key2不成立,而key2<key1也不成立时,key1和key2只能相等的,这就是严格弱序的非自反性。

比较条件表示如下:

  • key1小于key2:key1 < key2

  • key1大于key2:key2 < key1

  • key1等于key2:!(key1 < key2) && !(key2 < key1)

“invalid comparator”异常为何出现?

了解了上面的知识点后,我们以开头的例子分析为什么会出现”invalid comparator”异常,两个key分别为:key1(1,2)和key2(2,1),比较函数为:

bool operator<(const MyKey& key) const {
  return num1_ < key.num1_ ? true : num2_ < key.num2_;
}

我们可以得到:

key1小于key2key1大于key2
key1 < key2key2 < key1
truetrue

可以看到,key1既大于key2又小于key2,这完全就是两个自相矛盾的条件,这就是std::map报无效比较器的原因。那么比较器正确的写法是什么呢?

比较器operator<()的正确写法

依然以开头demo为例修改operator<()如下:

bool operator<(const MyKey& key) const {
  if (num1_ != key.num1_)
    return num1_ < key.num1_;

  if (num2_ != key.num2_)
    return num2_ < key.num2_;

  return false;
}
    文章作者:xuwenyan
    版权声明:本文为本站原创文章,转载请注明出处,非常感谢,如版权漏申明或您觉得任何有异议的地方欢迎与本站取得联系。

    扫描二维码推送至手机访问。

    版权声明:本文由艺文笔记发布,如需转载请注明出处。

    本文链接:https://www.xuwenyan.com/archives/2783

    标签: C++编程
    分享给朋友:

    “std::map自定义key,非严格弱序导致"invalid comparator"异常” 的相关文章

    C++实现任务栏图标进度条显示

    C++实现任务栏图标进度条显示

    ITaskbarList3* m_pTaskBarlist; VOID CALLBACK OnTimer(HWND hWnd, UINT, UINT_PTR id, DWORD) { if (id == 1) { static int progress = 0; if (pro...

    7z的简介和使用

    7z的简介和使用

    7z是一个支持多种压缩格式的开源项目,由Igor Pavlov开发,源码下载位置:https://www.7-zip.org/download.html源码结构项目源码目录结构是如下图:Asm包含主要算法实现的汇编代码,直接使用汇编的好处是可以提高运行效率,当然这对跨平台的支持不是很好。C主要是算法...

    vs(vs2015)拖动或停靠窗口崩溃的解决方法

    vs(vs2015)拖动或停靠窗口崩溃的解决方法

    使用vs拖动窗口后想要停靠某一个区域时,ide会崩溃重启,而且会反复这样,拖动布局功能基本丧失,使用起来非常不爽。解决办法如下: vs2019(visual studio 2019) 如果vs版本是2019,那么直接升级vs即可解决问题。 vs2015(visual studio...

    C++实现win32窗口文件拖拽

    C++实现win32窗口文件拖拽

    如题,C++如何实现win32窗口文件拖拽,直接上代码 方法1:使用win32消息实现 此方法的弊端在于,无法过滤可以拖拽的文件,拖拽时显示的图标也默认的,无法像资源管理器那样自定义。实现步骤大致分为以下三步: 第一步:首先创建窗口时必须在exStyle加上WS_EX_ACCEP...

    解决程序在xp系统总是莫名奇妙的崩溃问题(/Zc:threadSafeInit- )

    解决程序在xp系统总是莫名奇妙的崩溃问题(/Zc:threadSafeInit- )

    现象:程序在xp系统上面总是莫名其妙的崩溃,检查代码看不出任何问题,感觉代码都很好。即使你远程调试,找到了崩溃的点,当你注释了崩溃点之后,还是会崩溃到别的地方。当你遇到了这种情况的时候,不妨参照一下下面的方法看看,说不定可以解决问题。如何解决?将崩溃程序相关的所有工程代码全部关闭全局变量的线程安全检...

    ATL实现windows右键菜单扩展(ContextMenu)

    ATL实现windows右键菜单扩展(ContextMenu)

    右键菜单,即用户右击shell对象时弹出的上下文菜单(context menu)。本文记录了如何创建右键菜单的基本过程,跟着步骤一步一步来,即可创建出一个右键菜单工程。第一步,新建一个ATL工程Visual Studio—>新建项目—>ATL—>使用默认配置(一直按下一步即可)。注...