当前位置: 首页 > 知识库问答 >
问题:

为什么使用std::less作为默认仿函数来比较std::map和std::集中的键?

吕宣
2023-03-14

我想知道为什么std::map和std::set使用std::less作为比较键的默认函子。为什么不使用一个类似strcmp的函子呢?类似于:

  template <typename T> struct compare
  {
     // Return less than 0 if lhs < rhs
     // Return 0 if lhs == rhs
     // Return greater than 0 if lhs > rhs
     int operator()(T const& lhs, T const& rhs)
     {
        return (lhs-rhs);
     }
  }

假设地图中有两个对象,分别是键1和键2。现在,我们要插入另一个具有键3的对象。

如果std::map::插入使用比较,那么只需一个调用就有足够的信息来做出正确的决定。

根据映射中键的类型,std::less::operator()(键1,键2)可能很昂贵。

除非我缺少一些非常基本的东西,否则不应该使用类似于比较的东西,而不是作为比较键的默认函子?

共有2个答案

楚天宇
2023-03-14

基于树的容器只需要严格的弱总排序。

https://www.sgi.com/tech/stl/StrictWeakOrdering.html

>

  • 写访问

    映射和集合的插入点完全由单个二进制搜索确定,例如lower_boundupper_bound。二进制搜索的运行时复杂性为O(log n)

    读取访问权限

    这同样适用于搜索:搜索比线性等式扫描效率要高得多,因为大多数元素不需要进行比较。诀窍在于容器是订购的。

    结果是,不需要存在平等信息。只是,这些项目可以具有同等的顺序。

    实际上,这只意味着对元素类型的约束更少,在常见使用场景中实现需求和最佳性能的工作更少。总会有取舍。(例如,对于大型集合,哈希表(无序集和映射)通常更有效。请注意,它们确实需要相等的元素,并且使用哈希方案进行快速查找)

  • 翁昊乾
    2023-03-14

    我决定向Alexander Stepanov(STL的设计师)询问这件事。我可以引用他的话如下:

    最初,我提出了三方比较。标准委员会要求我改为标准比较运算符。我做了别人告诉我的事。20多年来,我一直主张在标准中添加3路组件。

    但请注意,也许不太直观,双向并不是一个巨大的开销。你不必做两倍的比较。在向下的过程中,每个节点只有一个比较(没有等式检查)。代价是不能提前返回(当键位于非叶中时)和在最后进行一次额外的比较(交换参数以检查相等性)。如果我没记错的话

    1 + 1/2*1 + 1/4*2 + 1/8*3 + ...
    = 1 + 1/2+1/4+1/8+... + 1/4+1/8+... + ...
    -> 3  (depth -> infty)
    

    在包含查询元素的平衡树上平均进行额外比较。

    另一方面,3路比较没有可怕的开销:无分支3路整数比较。现在,另一个问题是,在每个节点上用一个额外的分支来检查与0(相等)的比较结果,其开销是否比在最后额外进行3次比较要小。可能没什么大不了的。但我认为比较本身应该是三值的,这样就可以改变是否使用所有三种结果的决定。

    更新:请参阅下面的评论,了解为什么我认为三向比较在树中更好,但在平面阵列中不一定更好。

     类似资料:
    • 在过去的几个月里,我一直在学习C语言并使用终端。我的代码使用g和c11编译并运行得很好,但在过去几天里它开始出现错误,此后我在编译时遇到了问题。我唯一可以编译和运行的程序依赖于旧的C标准。 我第一次遇到的错误包括 尝试使用ecg$g-o stoi_试验stoi_试验编译。cpp-std=c 11 大堆cpp:13:22:错误:命名空间“std”中没有名为“stoi”的成员;你是说“阿托伊”吗?in

    • 为什么 首先返回最大的元素(即是最大优先级队列),即使它使用 作为比较类型? 当我想创建一个最小队列时,这尤其让我感到困惑,这将由 是您的最大值。如果您使用创建优先级队列,那么前面是最小的值。我意识到优先级队列使用堆,但我觉得这是有充分理由的。

    • https://godbolt.org/z/P97MaK 我玩的概念和预期d::is_equality_comparable工作矢量,但它没有。 编译错误在 内部失败,而不是在受概念保护的函数边界处失败。 这是错误还是预期行为?

    • 我在理解条件变量及其在互斥体中的使用时遇到了一些困难,我希望社区能帮助我。请注意,我来自win32背景,因此与CRITICAL_SECTION、HANDLE、SetEvent、WaitForMultipleObject等一起使用。 这是我第一次尝试使用C++11标准库进行并发操作,它是在这里找到的一个程序示例的修改版本。 关于这个的几个问题。 我读过“任何要等待std::condition_var

    • 我知道了从< code>std::async返回的< code>future具有某种特殊共享状态的原因,通过这种状态,< code >等待返回的future发生在future的析构函数中。但是当我们使用< code>std::pakaged_task时,它的未来不会表现出同样的行为。要完成打包的任务,必须从< code>packaged_task显式调用< code>future对象上的< cod

    • 我是个新手,一直在浏览源代码,发现: 这个函数定义这样的内部函数有什么原因吗?为什么不直接写: