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

三元比较算子是否总是高效的?

邢寒
2023-03-14

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

基于<=>及其返回类型:该模型具有主要优点,与以前针对C++的建议和其他语言的功能相比,该建议具有一些独特之处:

[...]

(6)效率,包括最终实现对比较的零开销抽象:绝大多数的比较总是单程通过的。唯一的例外情况是在同时支持偏序和相等的类型的情况下生成<=>=。对于<,单次传递对于实现零开销原则以避免重复相等比较至关重要,例如对于struct Employee{string name;/*more members*/};struct out{employeee;/*more members*/}中使用的-今天的比较违反了零开销抽象,因为Outer上的运算符<执行冗余的相等比较,因为它执行if(e!=that.e)返回e 遍历正如Kaminski所指出的,零开销抽象是C++的一个支柱,首次实现它用于比较是这种基于 <=>的设计的一个显著优势。

但接下来他给出了这个例子(第6页第1.4.5节):

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 ...
};

运算符>(a,b)定义为a<=>b>0不会导致很大的开销吗?(尽管形式与他所讨论的不同)。该代码将首先测试相等性,然后测试less,最后测试greater,而不是只直接测试greater

我是不是漏了什么?


共有3个答案

林龙野
2023-03-14

一般说来,当您处理一个同时进行所有比较的类型时,重载<=>是有意义的,因为这种类型要么只会稍微增加一些成本,要么与以不同方式进行比较的成本相同。

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

所以对于像这样的基本类型,你或多或少都没问题。

当您处理像树排序这样的事情时,您确实需要预先知道您关心的是哪个操作。如果您所要求的只是==,那么您真的不想仅仅为了知道它们是不相等的,就必须搜索树的其余部分。

但我个人...我从来不会用比较运算符来实现像树排序这样的东西。为什么?因为我认为这样的比较应该是逻辑上快速的操作。而树搜索是一个如此缓慢的操作,您真的不想在偶然的情况下或在绝对必要的情况下执行它。

看看这个案子就知道了。说家谱中的一个人“比”另一个人“少”,究竟是什么意思?意思是一个是另一个的孩子。直接用is_transitive_child_of来问这个问题不是更易读吗?

你的比较逻辑越复杂,你所做的事情就越不可能是真正的“比较”。这个“比较”操作可能会有一些文本描述,这样会更容易读懂。

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

但无论如何,<=>是比较的零开销抽象吗?否;您可以构造这样的情况:计算排序的成本比检测您所要求的特定关系的成本要高得多。但就个人而言,如果是这样的话,很有可能根本就不应该通过运算符来比较此类类型。

岳凯康
2023-03-14

大的定义。存在开销,因为在偏序中,a==Biffa<=BB<=a。复杂度与拓扑排序相同,O(V+E)。当然,现代C++的方法是编写安全、干净和可读的代码,然后进行优化。您可以选择首先实现宇宙飞船操作符,然后在确定性能瓶颈后进行专门化。

魏航
2023-03-14

运算符>(a,b)定义为a<=>b>0不会导致很大的开销吗?

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

但是,该提案并没有建议移除其他比较运算符,以利于<=>:如果您想重载其他比较运算符,您可以自由地去做:

要笼统:不要限制固有的东西。不要随意限制一套完整的用途。避免特例和局部特征。-例如,本文支持所有七个比较运算符和操作,包括通过<=>添加三向比较。它还支持所有五大比较类别,包括偏订单。

 类似资料:
  • #非技术面试记录# 运营岗 12-投递 13-笔试 20-一面 23-hr面 简单分享下一面面经😊 自我介绍 选择公司看重哪些 为什么选择多益 运营有哪些能力要求 玩过什么游戏这半年 他做了什么好的运营策略和差的运营策略 如何起号 你会选择一个什么平台做宣传 对工作城市偏好 平时在学校都干什么 玩过多益的游戏吗 #本周投递记录# 面了1h,口干舌燥😂

  • C++20中有一个新的比较运算符。然而,我认为在大多数情况下,一个简单的减法效果很好: 它们的效果是一样的。我真的不明白这有什么区别。

  • 考虑以下两个用于的重载: 如果我将对象与进行比较,将为我生成正确的运算符,所以像、

  • 当我试图学习C++运算符的时候,我无意中在cppreference.com上发现了一个奇怪的比较运算符,*在如下表中: “好吧,如果这些是C++中常见的运算符,我最好学会它们”,我想。但我想要弄清楚这个谜团的一切努力都没有成功。即使在这里,在堆栈溢出,我没有运气在我的搜索。 如果有的话,这个运算符到底是做什么的? *同时CPPreference.com更新了该页,现在包含有关运算符的信息。

  • 问题内容: 如果值大于零,如何比较? 问题答案: 就像这样简单: 的文档实际上指定了它将返回-1、0或1,但是更通用的方法仅保证在适当的三种情况下小于零,零或大于零- 因此,我通常只是坚持这种比较。

  • 如何比较两个javascript集?我尝试使用和但都返回false。 这两个集合是等价的,因为根据定义,集合没有顺序(至少通常没有)。我看了MDN上的集合的留档,没有发现任何有用的东西。有人知道怎么做吗?