C++ 为什么使用std::less作为默认函子来比较std::map和std::set中的键?
我想知道为什么C++ 为什么使用std::less作为默认函子来比较std::map和std::set中的键?,c++,stdmap,stdset,C++,Stdmap,Stdset,我想知道为什么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 ope
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);
}
}
模板结构比较
{
//如果lhsrhs,返回值大于0
int运算符()(T常量和lhs、T常量和rhs)
{
返回(左侧和右侧);
}
}
假设map
中有两个对象,其中包含键key1
和key2
。现在,我们要插入另一个具有键key3
的对象
使用std::less
时,insert
函数需要首先使用key1
和key3
调用std::less::operator()
。假设std::less::operator()(键1,键3)
返回false。它必须在切换键的情况下再次调用std::less::operator()
,std::less::operator()(key3,key1)
,以确定key1
是否等于key3
或key3
大于key1
。有两个调用std::less::operator()来决定第一个调用是否返回false
如果使用了std::map::insert
,compare
,那么只需一次调用就有足够的信息来做出正确的决策
根据映射中键的类型,std::less::operator()
除非我缺少一些非常基本的东西,否则std::map
和std::set
不应该使用类似compare
的东西,而不是std::less
作为比较键的默认函数吗?基于树的容器只需要严格的弱总排序
看
写访问
地图和集合的插入点完全由单个二进制搜索确定,例如下限
或上限
。二进制搜索的运行时复杂性是O(logn)
读访问
同样的道理也适用于搜索:搜索比线性相等扫描的效率要高得多,这正是因为大多数元素不需要进行比较。诀窍在于容器是有序的
结果是平等性信息不需要存在。只是,项目可以具有同等的顺序
在实践中,这仅仅意味着对元素类型的约束更少,在常见使用场景中实现需求和最佳性能的工作量更少。总会有取舍。(例如,对于大型集合,哈希表(无序集和映射)通常效率更高。请注意,这些确实需要相等的元素,并且它们采用哈希方案进行快速查找)我决定向Alexander Stepanov(STL的设计师)询问这一点。我可以引用他的话如下:
最初,我提出了三方比较。标准委员会问道
我将更改为标准比较运算符。我照吩咐的做了。
我一直主张将3向组件添加到
20多年了
但请注意,也许不直观地说,双向并不是一个巨大的开销。你不必做两倍的比较。在下降过程中,每个节点仅进行一次比较(无相等检查)。代价是不能提前返回(当键在非叶中时)并在最后进行一次额外的比较(交换参数以检查相等性)。如果我没弄错的话,那就是
1 + 1/2*1 + 1/4*2 + 1/8*3 + ...
= 1 + 1/2+1/4+1/8+... + 1/4+1/8+... + ...
-> 3 (depth -> infty)
在包含查询元素的平衡树上平均进行额外比较
另一方面,三方比较没有可怕的开销:。现在,另一个问题是,在每个节点上用一个额外的分支来检查与0(相等)的比较结果是否比在最后额外进行3次比较的开销小。可能没什么大不了的。但我认为比较本身应该是三值的,这样是否使用所有三种结果的决定就可以改变
更新:请参阅下面的评论,了解为什么我认为三向比较在树中更好,但在平面阵列中不一定更好。不是std::set
和std::map
都是隐藏的RB树吗?但这意味着对于希望使用映射或映射的任何数据类型,都必须重载-
操作符set@Smac89,这是真的。同样正确的是,您必须实现operator@Paranaix我认为这在技术上是一个实施细节。但是,如果您深入到标准规范的底部,您可能会发现很难找到另一个仍然满足标准指定的性能约束的实现。@在AIX中,标准不要求任何特定的结构,只要求性能保证。返回<0
<0的比较函数,==0
或>0
可用于任何小于的比较函数,只要返回0
(如果)!减去(a,b)&!减去(b,a)
。它同样具有表现力,没有任何类型可以有可用的less
函数,但没有可用的compare
函数。@hvd这是重言式。然而,在实践中,操作符=
通常用于表示相等语义,而不是等价。将两者混合在一起将是非常令人惊讶的。并非所有具有总排序的类型都必须实现相等。是的,您是对的,我已经编辑了以前的注释以删除对operator==
的引用。但是比较两个项目并根据比较结果返回负、零或正的函数已经被广泛使用,其中零并不意味着相等。一个非常常见的例子是不区分大小写的字符串比较。很好的一点,@hvd。你说得对。这是一个比较