web-dev-qa-db-ja.com

宇宙船演算子は常に効率的ですか?

ハーブサッターは、彼の 「宇宙船」オペレーターの提案 (セクション2.2.2、12ページの下部)で次のように述べています。

_<=>_とその戻り値の型にすべてを基づかせます:このモデルには大きな利点があり、C++や以前の提案と比較してこの提案に固有の利点がいくつかあります。他の言語の機能:

[...]

(6)比較のためのオーバーヘッドゼロの抽象化を最終的に達成することを含む効率:比較の大部分は常にシングルパスです。唯一の例外は、半順序と等式の両方をサポートするタイプの場合、_<=_と_>=_が生成されます。 _<_の場合、_struct Employee { string name; /*more members*/ };_で使用される_struct Outer { Employeee; /*more members*/ };_のように、ゼロオーバーヘッドの原則を達成して等価比較を繰り返さないようにするには、シングルパスが不可欠です。今日の比較は、_operator<_ on Outerは、_e.name_の等しいプレフィックスを2回トラバースするif (e != that.e) return e < that.e;を実行するため、冗長な等価比較を実行します(名前が等しい場合は、等しいプレフィックスをトラバースします) Employeeの他のメンバーの2回も)、これは一般的に最適化することはできません。 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をテストします。

ここで何かが足りませんか?

20
Cris Luengo

operator>(a,b)a<=>b > 0として定義しても、大きなオーバーヘッドは発生しませんか?

それはsomeオーバーヘッドにつながります。ただし、オーバーヘッドの大きさは相対的です。プログラムの残りの部分と比較して比較の実行コストがごくわずかである状況では、5つではなく1つの演算子を実装することでコードの重複を減らすことは、許容できるトレードオフになる可能性があります。

ただし、この提案では、<=>を優先して他の比較演算子を削除することは提案されていません。他の比較演算子をオーバーロードする場合は、自由に実行できます。

一般的に:固有のものを制限しないでください。使用の完全なセットを恣意的に制限しないでください。特別な場合や部分的な機能は避けてください。 –たとえば、このペーパーでは、<=>による3者間比較の追加を含め、7つの比較演算子と演算すべてをサポートしています。また、半順序を含む5つの主要な比較カテゴリすべてをサポートします。

10
dasblinkenlight

大のいくつかの定義について。半順序では、_a == b_ _a <= b_および_b <= a_の場合、オーバーヘッドが発生します。複雑さはトポロジカルソートO(V+E)と同じです。もちろん、最新のC++アプローチは、安全でクリーンで読みやすいコードを記述し、その後最適化することです。最初に宇宙船オペレーターを実装し、パフォーマンスのボトルネックを特定したら専門化することを選択できます。

7
user9163035

一般的に言って、<=>のオーバーロードは、すべての比較を一度に実行する方がわずかに費用がかかるか、異なる方法で比較するのと同じ費用がかかるタイプを扱っている場合に意味があります。

文字列の場合、2文字の各ペアを減算する必要があるため、<=>は単純な==テストよりもコストがかかるように見えます。ただし、すでに文字の各ペアをメモリにロードする必要があるため、その上に減算を追加するのは簡単な費用です。実際、2つの数値の同等性の比較は、コンパイラーによって減算およびゼロに対するテストとして実装される場合があります。そして、それを行わないコンパイラーであっても、ゼロに対する減算と比較は、おそらく大幅に効率が低下することはありません。

したがって、そのような基本的なタイプの場合、多かれ少なかれ問題ありません。

ツリーの順序付けなどを扱う場合は、どの操作を気にするかを事前に知っておく必要があります。要求したのが==だけだった場合、本当にツリーの残りの部分を検索して、それらが等しくないことを知る必要はありません。

しかし、個人的には...そもそも比較演算子を使ったツリー順序付けのようなものを実装することは決してありません。どうして?そのような比較は論理的に速い操作であるべきだと思うからです。ツリー検索は非常に遅い操作であるため、本当に偶然または絶対に必要な場合以外はいつでも実行したくありません。

このケースを見てください。家系図の人が他の人より「少ない」と言うのは本当にどういう意味ですか?これは、一方が他方の子であることを意味します。 is_transitive_child_ofで直接その質問をする方が、コードで読みやすくなりませんか?

比較ロジックが複雑になるほど、実行していることが実際に「比較」である可能性は低くなります。この「比較」操作を呼び出すことができ、より読みやすくなるというテキストによる説明がある可能性があります。

確かに、そのようなクラスは、2つのオブジェクト間の関係を表すpartial_orderを返す関数を持つことができます。しかし、私はその関数をoperator<=>とは呼びません。

しかし、いずれにせよ、<=>は比較のオーバーヘッドゼロの抽象化ですか?番号;要求した特定の関係を検出するよりも、順序を計算する方が大幅にコストがかかるケースを作成できます。しかし、個人的には、そのような場合は、演算子を使用してそのようなタイプを比較するべきではない可能性がありますまったく

6
Nicol Bolas