三向比较运算符总是有效吗?

时间:2018-01-02 18:22:26

标签: c++ operators c++20 spaceship-operator

Herb Sutter在他的proposal for the "spaceship" operator(第12.2节,第12页,第12页)中说:

  

基于<=>及其返回类型的所有内容:此模型具有主要优势,与之前的C ++提案和其他语言的功能相比,此提案有一些独特优势:

     

[...]

     

(6)效率,包括最终实现用于比较的零开销抽象:绝大多数比较总是单通道。在支持部分排序​​和相等的类型的情况下,唯一的例外是生成<=>=。对于<,单次传递对于实现零开销原则至关重要,以避免重复相等比较,例如struct Employee { string name; /*more members*/ };中使用的struct Outer { Employeee; /*more members*/ }; - 今天的比较违反了零开销抽象,因为{ operator<上的{1}}执行冗余的相等比较,因为它执行Outer遍历等号的前缀   if (e != that.e) return e < that.e;两次(如果名称相同,则两次遍历e.name的其他成员的相等前缀),这通常无法优化。正如Kamiński所指出的,零开销抽象是C ++的一个支柱,并且首次实现它的比较是基于Employee的这种设计的一个重要优势。

但是他给出了这个例子(第1.4.5节,第6页):

<=>

class PersonInFamilyTree { // ... public: std::partial_ordering operator<=>(const PersonInFamilyTree& that) const { if (this->is_the_same_person_as ( that)) return partial_ordering::equivalent; if (this->is_transitive_child_of( that)) return partial_ordering::less; if (that. is_transitive_child_of(*this)) return partial_ordering::greater; return partial_ordering::unordered; } // ... other functions, but no other comparisons ... }; 定义为operator>(a,b)不会导致大量开销? (虽然形式与他讨论的不同)。该代码首先测试相等性,然后测试a<=>b > 0,最后测试less,而不是直接测试greater

我在这里错过了什么吗?

3 个答案:

答案 0 :(得分:10)

  

operator>(a,b)定义为a<=>b > 0不会导致大量开销吗?

这会导致一些开销。然而,开销的大小是相对的 - 在运行比较的成本相对于程序的其余部分可忽略不计的情况下,通过实现一个运算符而不是五个运算符来减少代码重复可能是可接受的权衡。

但是,该提案并未建议删除其他比较运算符以支持<=>:如果您想要重载其他比较运算符,您可以自由地执行此操作:

  

一般:不要限制固有的内容。不要随意限制一整套用途。避免特殊情况和部分功能。 - 例如,本文支持所有七个比较运算符和运算,包括通过<=>添加三向比较。它还支持所有五个主要比较类别,包括部分订单。

答案 1 :(得分:7)

对于大的一些定义。由于部分排序a == b iff a <= bb <= a,因此存在开销。复杂性与拓扑排序O(V+E)相同。当然,现代C ++方法是编写安全,干净和可读的代码,然后然后优化。您可以先选择实施宇宙飞船运营商,然后在确定性能瓶颈后进行专业化。

答案 2 :(得分:6)

一般来说,当您处理一种类型的情况时,重载<=>是有意义的,其中一次性进行所有比较要么只是更加昂贵,要么与以不同方式进行比较的成本相同。

对于字符串,<=>似乎比直接==测试更昂贵,因为您必须减去每对两个字符。但是,由于您已经必须将每对字符加载到内存中,因此在其上添加减法是一项微不足道的费用。实际上,比较两个数字的相等性有时会被编译器实现为减法和零测试。即使对于那些不这样做的编译器,减法和与零进行比较的效率也可能没那么低。

因此对于像这样的基本类型,你或多或少都很好。

当你处理树木订购之类的事情时,你真的需要事先知道你关心的操作。如果你要求的只是==,那么

>

但是我个人......我永远不会用比较运算符来实现像树排序这样的东西。为什么?因为我认为这样的比较应该是逻辑上快速的操作。树搜索是一个如此缓慢的操作,你真的不想偶然或在绝对必要之外的任何时间进行搜索。

看看这个案子。说一个家谱中的人是否小于&#34;这是什么意思呢?另一个?这意味着一个人是另一个人的孩子。如果只是直接使用is_transitive_child_of提出问题,代码是否更具可读性?

您的比较逻辑越复杂,您正在做的事情就越不可能真正进行比较&#34;。可能有一些文字描述,这&#34;比较&#34;可以调用更具可读性的操作。

哦,当然,这样的类可以有一个返回表示两个对象之间关系的partial_order的函数。但我不会将该函数称为operator<=>

但无论如何,<=>是一个零开销的比较抽象?没有;您可以构建一些案例,其中计算订单的成本要比检测您要求的特定关系的成本高得多。但就个人而言,个人很有可能不应该通过运营商来比较这些类型。