28

Herb Sutter 在他对“宇宙飞船”操作员的提议(第 2.2.2 节,第 12 页底部)中说:

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

[...]

(6)效率,包括最终实现比较的零开销抽象:绝大多数比较总是单通的。唯一的例外是在同时支持部分排序​​和相等的类型的情况下<=生成。>=For <,single-pass 对于实现零开销原则以避免重复相等比较是必不可少的,例如 for struct Employee { string name; /*more members*/ };used in struct Outer { Employeee; /*more members*/ };- 今天的比较违反了零开销抽象,因为operator<onOuter执行冗余相等比较,因为它执行if (e != that.e) return e < that.e;遍历相等前缀 e.name两次(如果名称相等,则遍历其他成员的相等前缀Employee两倍),这通常不能被优化掉。正如 Kamiński 所指出的,零开销抽象是 C++ 的支柱,第一次实现它以进行比较是这种基于<=>.

但随后他给出了这个例子(第 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测试 ,而不是仅直接测试greater.

我在这里错过了什么吗?

4

3 回答 3

11

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

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

但是,该提案不建议删除其他比较运算符以支持<=>:如果您想重载其他比较运算符,您可以自由地这样做:

笼统地说:不要限制固有的东西。不要随意限制一整套用途。避免特殊情况和部分特征。– 例如,本文支持所有七种比较运算符和操作,包括通过添加三路比较<=>。它还支持所有五个主要比较类别,包括偏序。

于 2018-01-02T18:32:22.383 回答
10

对于一些大的定义。存在开销,因为在部分排序中,a == b当且仅a <= bb <= a. 复杂性与拓扑排序相同,O(V+E). 当然,现代 C++ 方法是编写安全、干净和可读的代码,然后进行优化。您可以选择先实现 spaceship 运算符,然后在确定性能瓶颈后进行专门化。

于 2018-01-02T18:39:45.457 回答
6

一般来说,<=>当您处理一种类型时,重载是有意义的,其中一次进行所有比较要么只是稍微贵一点,要么与以不同方式比较它们具有相同的成本。

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

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

当您处理诸如树排序之类的事情时,您确实需要预先知道您关心哪个操作。如果您要求的只是==,您真的不想为了知道它们不相等而搜索树的其余部分。

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

看看这个案例。说家谱中的一个人“小于”另一个人究竟是什么意思?这意味着一个是另一个的孩子。直接用 来问这个问题不是在代码中更具可读性is_transitive_child_of吗?

您的比较逻辑越复杂,您所做的事情就越不可能是真正的“比较”。可能有一些文本描述可以调用这个“比较”操作,这将更具可读性。

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

但无论如何,是<=>零开销的比较抽象吗?不; 您可以构建计算顺序的成本明显高于检测您要求的特定关系的案例。但就个人而言,如果是这样的话,你很有可能根本不应该通过运算符来比较这些类型。

于 2018-01-12T19:54:57.193 回答