私は最近、比較に関するシリーズを作成し、3 者間の比較のために今後の宇宙船オペレーターを使用するためのガイドラインをいくつか提供しました.特に、現在の設計のいくつかの欠陥を指摘しました.
さて、次の C++ 標準化会議の提案がここにあります — ほぼ 300 件あります。数えてみると、そのうち 11 件は宇宙船のオペレーターを扱っています。
それでは、それらを見て、私が指摘した問題のいずれかが修正されるかどうかを見てみましょう。
05
の使用によるパフォーマンスへの影響 平等のために
素晴らしい名前の P1190 — 「私はこれを注文していません!」 — 18
を使用した場合の影響について詳しく説明します 平等が必要な場合は、最後の部分で簡単に説明しましたが、基本的な問題は次のとおりです。
template <typename T>
auto operator<=>(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
auto lhs_cur = lhs.begin();
auto lhs_end = lhs.end();
auto rhs_cur = rhs.begin();
auto rhs_end = rhs.end();
for (; lhs_cur != lhs_end && rhs_cur != rhs_end; ++lhs_cur, ++rhs_cur)
{
// compare each member
auto cmp = *lhs_cur <=> *rhs_cur;
if (cmp != 0)
// they aren't equal, so return that as the result
return cmp;
// otherwise continue
}
// at this point all members in the common prefix are equal
if (lhs_cur != lhs_end)
// lhs is bigger, so it's greater
return std::strong_ordering::greater;
else if (rhs_cur != rhs_end)
// lhs is smaller, so it's less
return std::strong_ordering::less;
else
// both are completely equal
return std::strong_ordering::equal.
}
上記は、27
の宇宙船オペレーターの可能な実装です。 :これは、std::lexicographical_compare_3way が行うように、単純に辞書式の 3 者間比較を行います。
その定義を使用すると、 35
を実行できます コンパイラはそれを 48
に書き換えます .
しかし、 59
もできます コンパイラはそれを 66
に書き換えます .これは理想的ではありません!
コンテナが等しいかどうかだけを比較したい場合は、最初にサイズを確認します 、最後ではありません:2 つのコンテナーのサイズが異なる場合、それらを等しくすることはできないため、ループは必要ありません。
これは 76
と書くことを意味します コンテナの場合、84
も必要です。 パフォーマンス上の理由から。そして 91
として 106
まで延期します 、 111
も必要です .したがって、1 つだけでなく 3 つのオペレーターが必要です — これは良いことですが、それでも理想的ではありません。
提案はいくつかの解決策を指摘していますが、明示的に提案していません.
パフォーマンスへの影響の修正
ここで P1185 の出番です。これは、次の 3 つの部分からなる問題に対する適切な解決策を提案します。
<オール> <リ>
123
のルックアップを変更 と 136
: 142
156
のみを検索します 163
ではなくオーバーロード .しかし、それでも対称的に行われるので、必要なのは 174
だけです 、型を逆にした追加バージョンではありません。同様に、189
196
を試します または 206
215
ではありません .これにより、 228
を書くことができます と 238
効率を最大限に高めます。
242
を生成 256
生成時 : ただし、上記の修正には残念な結果があります。 、あなたは平等ではなく、順序付けのみを取得します。したがって、論文には、デフォルトの宇宙船オペレーターもデフォルトの272
を生成するというオプションの提案があります 、再び 1 つのデフォルト宣言で完全な順序付けと等価性を実現します。
デフォルトの比較演算子の実装を修正: デフォルトの 281
294
にディスパッチされただけでは役に立ちません もう一度!デフォルトの 304
317
を使用してすべてのメンバーの辞書式比較を行います 、デフォルトの 321
すべてのメンバーを 331
と比較します 343
で連鎖された結果を返します .そうすれば、より効率的な 355
を実際に拾うことができます。 コンテナの種類の数!
この提案では、コンテナー タイプの作成者は次の 2 つのことを行う必要があります。
<オール>364
を書く .370
を書く .その後、すべての比較演算子が機能し、可能な限り高速になります。
そして、単純なクラスの作成者は、以前のように宇宙船演算子をデフォルトにするだけで、より高速な等値演算子を自動的に取得できます!
383
の一般的なスペル 396
じゃないですか
400
を見てください 414
の実装 再び上で与えられた:423
を呼び出して、各メンバーの辞書式比較を行います。 .
前に言ったように、それは間違っています。
431
の場合 型に 447
がない場合はコンパイルされません ただし、459
のみ と 469
.そして今のところ、478
を持つ型はありません !
したがって、一般的なコードでは 489
を使用できません 直接、それを試して 497
を使用するようにフォールバックする必要があります および 505
3 者間比較用。少なくとも 517
はあります
しかし、522
の一般的なスペルが間違っているのは本当に残念です。 535
です .
P1186 は 545
に同意し、提案します 558
へのフォールバックを自動的に行う必要があります と 569
.そうすれば、常に 579
を使用できます
そのときの名前は 587
再び利用可能になった場合、代わりに関数オブジェクトになることを提案しています:Where 592
605
を実行します 比較、614
621
を実行します 比較。
比較シリーズの第 5 部でも同様に実装し、637
と呼びました。 .
デフォルトの順序
ただし、P0891 は別の名前に似た名前を付けたいと考えています。
648
のように正しい順序を提供できない型があります。 .652
があるというだけでは意味がありません 順序付けが数学的特性と互換性がないためです。
それでも、667
を使用するのはまったく合理的です。 マップのキーとして。そのためにはいくつか必要です 賢明ではありません。
同様に 678
を使用 マップ内のキーにより、より効率的な順序付けも可能になるため、最初に長さで並べ替え、次に各要素を並べ替えます。長さの異なるコンテナがたくさんある限り、比較は依然として高速です。結果の順序付けはあまり役に立ちません。 、しかしそうである必要はありません — 有効なものである必要があります.
だから 681
実際には 693
を使用しないでください (または 707
) 直接、別のカスタマイズ ポイントを使用する必要があります。
これが論文の提案です。新しいカスタマイズ ポイントは 719
と呼ばれます。 型のデフォルトの順序を返します。727
を持たない型に提供できます。 とにかくコンテナ内でそれらを使用することを許可します.
比較シリーズのパート 5 では、734
と呼びました。 .
以前の提案が両方とも受け入れられた場合、それは次のことを意味します:
- <リ>
ジェネリックコードで何かが等しいかどうかをチェックしたい場合は、 749
を使用してください .それはできるだけ速くなり、三者間比較に書き直されません.
3 者間比較を行う場合は、754
を使用します 763
に手動でフォールバックする必要はありません。 または 779
.
3 者間比較を関数オブジェクトとして行う必要がある場合は、785
を使用します。 .それはちょうど 799
のようなものです 806
の場合 または 817
822
の場合 .
いくつかが必要な場合 タイプの順序付けには、837
を使用します .並べ替えと二分探索だけが必要な場合は、任意の順序付けを実装します。
標準ライブラリ型には 849
がありません
宇宙船の提案では 858
が追加されましたが、 861
のような組み込み型に 、それらを標準ライブラリに追加しませんでした。現在のセマンティクスは 871
です 3者間比較で使用できないため、これは悪いことです!
したがって、P0790 は 885
の追加を提案しています 現在 899
を持つすべての型へのオーバーロード または 904
.
自動フォールバックが受け入れられる場合、この追加は必要ないかもしれません。
ただし、まだ必要なのは P1191 です。これは、現在まったく比較を行わない 2 つの型に 3 者間比較 (したがって通常の比較) を追加することを提案しています。正確には、型の等価性のみを提案しています。 913
のように または非常に重要でよく使用される 922
.
その他のライブラリの改善
P1310 を引用すると、2 つの文字列を比較する場合は、次のようになります。
934
(947
を返します )950
(964
を返します )972
(984
を返します )998
(1007
を返します )1013
(1023
を返します )1030
(1043
を返します )1054
(1066
を返します )1074
(1080
を返します )1092
(1102
を返します )1112
(1123
を返します 、2 つのパスが同じファイルに解決されるかどうかの弱等号を提供します)
これは、さまざまな戻り値の型とそうでないものがあるため、少し混乱しています。
したがって、代わりに単一の統一 1137
が必要です 1148
を非推奨にすることに同意しないことに注意してください。 1150
に賛成 !詳細については、私の比較シリーズまたは P1307 をお読みください。
現在の標準ライブラリには 1166
のような概念があります または 1178
1188
で機能する .P1312 は、1191
でも動作することを提案しています。 および 1202
1212
のような名前付き比較関数として弱い順序付けを実装するという私のガイドラインに従うことができるため、これは非常に重要な変更です。 .次に、それらを 1221
に渡すだけです または 1235
手動でラップする必要はありません!
1248
のような概念の変更を提案していないことに注意してください。 1257
を使用するには 1264
として 1270
しかない型でも機能します .
いくつかの順序付けアルゴリズムを実装したとき、trait 1281
を書きました P1187 は 1295
という名前で提案しています。 .
そして最後に、P0863 は 1300
の潜在的なバグの修正について説明します .順序付けの背後にある数学の簡単な要約:半順序では、2 つの型は、小さい/大きい/等しいか、または順序付けされていない可能性があります。ただし、1311
1324
を返すことはありません !
結論
する これについて引用してください:
P1186 1333
なし 汎用コードではまったく役に立ちません。そして、P1185 は高速に不可欠です。 ジェネリック コード。コンセプトにより、ジェネリック コードは初心者にとってより簡単で親しみやすいものになるはずです。これ以上の落とし穴は必要ありません。
他の提案も有用ですが、これら 2 つは 1340
を本当に洗練するために重要です。 .C++20 に組み込まれることを心から願っています。