与三值比较函数相比,仅使用小于运算符进行排序

时间:2010-03-05 09:34:24

标签: c++ sorting stl operator-overloading spaceship-operator

在C ++ / STL中,只使用less-than运算符进行排序。尽管我不知道排序算法是如何实际实现的,但我认为其他操作是隐含的:

a > b *equals* b < a == true
a == b *equals* !(a < b) && !(b < a)

与使用trivalue * compare函数(例如Java)相比,这对性能有好处,或者为什么要做出这个设计决策呢?

我的假设是,任何trivalue compareto函数仍然必须实现这些比较,从而产生相同的性能。

**通过三值比较函数,我的意思是比较函数,它返回-1,0和1小于,等于和高于*

更新 似乎太空飞船<=>运算符来自C ++ 20,所以显然委员会认为仅使用operator<存在缺点。

3 个答案:

答案 0 :(得分:11)

从某种意义上说,另外两个是隐含的,但更准确的是说比较排序实际上并不需要一个三值比较器,并且C ++的排序以一种方式实现不使用一个以最小化比较器所需的行为。

std :: sort定义并专门使用这样的东西是错误的:

template <typename T, typename Cmp>
int get_tri_value(const T &a, const T &b, Cmp lessthan) {
    if (lessthan(a,b)) return -1;
    if (lessthan(b,a)) return 1;
    return 0;
}

...因为你在调用lessthan的次数方面最终会得到效率低下的算法。如果你的算法没有对1回报和0回报之间的差异做任何有用的事情,那么你就浪费了比较。

C ++指的是“严格的弱排序”。如果<是一个严格的弱排序,!(a < b) && !(b < a),则 <!}}它们只是在排序中“在同一个地方”,a == b是等价关系。因此!(a < b) && !(b < a)所需的比较器命令对象的等价类,它不提供总订单。

它唯一的区别就是你在sort时所说的话。对于严格的总订单,您可以推导出!(a < b),读取“小于或等于”。对于严格的弱订单,您无法将b <= a定义为b <= a,并且这是真的。 C ++对此很迂腐,因为它允许运算符重载它几乎必须如此,因为人们重载运算符需要术语,以便告诉用户他们的代码在运算符如何关联方面可以期待什么。 Java确实讨论了比较器和hashCode与equals一致,这就是你所需要的。 C ++必须处理&lt;,&gt;,==,&lt; =,&gt; =,赋值的后置条件,等等。

C ++在API中采用了相当纯粹的数学方法,因此所有内容都是根据单个二元关系定义的。 Java在某些方面更友好,并且更喜欢三向比较,其中基本单元的定义(比较)稍微复杂一些,但是由它引出的逻辑更简单。这也意味着排序算法在每次比较时获得更多信息,这有时是有用的。例如,请参阅“荷兰国旗”快速排序优化,这在数据中存在大量“在同一位置”重复时会带来好处。

在这种情况下,三值比较器是速度增益。但C ++使用比较器的一致定义进行排序,也使用b < a || b == asetmap等等,这几乎不受三值比较器的影响(可能保存一个比较,也许不吧)。我猜他们决定不为了特定或有限的潜在效率增益而使他们漂亮的通用界面复杂化。

答案 1 :(得分:1)

我在C ++中的猜测只是为了减少代码重复:一旦你在类/类型上定义了一个比较操作,你不仅可以通过简单地写一个&lt;来比较这些对象。 b,但也可以对这些对象进行排序。

至于排序,我们只需要少于运营商,为什么要引入额外的东西? :)

答案 2 :(得分:0)

如果你指的是std :: sort(),它只使用less()运算符,因为它不需要保留等效元素的相对排序,所以它只需要less()运算符,并且隐式更大( )运营商。

虽然std :: stable_sort会保留它,但速度较慢。它需要less()运算符和双向迭代器来交换equal()运算符来构造“trivalue”比较函数