[解決済み] C++20の動作は、等号演算子で既存のコードを壊すか?
質問
デバッグ中に以下のようなことに遭遇しました。 この質問 .
私はそれをわざわざ切り詰めて、単に ブースト演算子 :
-
#include <boost/operators.hpp> struct F : boost::totally_ordered1<F, boost::totally_ordered2<F, int>> { /*implicit*/ F(int t_) : t(t_) {} bool operator==(F const& o) const { return t == o.t; } bool operator< (F const& o) const { return t < o.t; } private: int t; }; int main() { #pragma GCC diagnostic ignored "-Wunused" F { 42 } == F{ 42 }; // OKAY 42 == F{42}; // C++17 OK, C++20 infinite recursion F { 42 } == 42; // C++17 OK, C++20 infinite recursion }
このプログラムは、GCCとClangの両方でC++17(ubsan/asan有効)でコンパイルし、正常に動作します。
-
を変更すると 暗黙の コンストラクタを
explicit
のように、明らかに問題のある行は はもはや C++17 でコンパイルできません。
意外と知られていない両者のバージョン は C++20 でコンパイルできます ( v1 と v2 ) が、これらは 無限再帰 (最適化レベルに応じてクラッシュまたはタイトループ) を引き起こし、C++17 ではコンパイルされません。
明らかに、C++20 にアップグレードすることによって忍び込むこの種のサイレント バグは心配です。
質問です。
- この c++20 の動作は適合していますか (適合していると思います)。
- 具体的に何が邪魔をしているのでしょうか。私はそれがc++20の新しい"spaceship operator"のサポートのためかもしれないと思うが、理解できない。 はどのように がこのコードの振る舞いを変えるのか理解できません。
どのように解決するのですか?
確かに、C++20 では残念ながらこのコードは無限に再帰的になってしまいます。
以下は縮小した例です。
struct F {
/*implicit*/ F(int t_) : t(t_) {}
// member: #1
bool operator==(F const& o) const { return t == o.t; }
// non-member: #2
friend bool operator==(const int& y, const F& x) { return x == y; }
private:
int t;
};
を見てみましょう。
42 == F{42}
.
C++17 では、候補は 1 つだけでした。非メンバーの候補 (
#2
) という 1 つの候補しかなかったので、それを選択します。その本体である
x == y
は、それ自体が1つの候補を持つだけです:メンバー候補 (
#1
) で、これは暗黙のうちに
y
を
F
. そして、そのメンバー候補は2つの整数のメンバーを比較し、これは全く問題ありません。
C++20では、最初の式
42 == F{42}
は、現在では
になりました。
の候補者がいます。非会員の候補者 (
#2
)、そして今度は逆メンバー候補(
#1
を反転させたもの) もあります。
#2
はより良いマッチです。変換を呼び出す代わりに両方の引数を正確にマッチさせるので、それが選択されます。
ところが、今は
x == y
には
に
の候補があります: 再びメンバー候補 (
#1
) だけでなく、逆向きの非会員候補 (
#2
を反転させたもの)。
#2
は,以前と同じ理由で,よりよくマッチします:変換の必要がないからです。そこで
y == x
を評価します。無限の再帰です。
非反転の候補は反転の候補より優先されるが、タイブレーカーとしてのみ。より良い変換シーケンスが常に最初になります。
なるほど、素晴らしい。どうすれば修正できるのでしょうか。最も簡単なオプションは、非会員候補を完全に削除することです。
struct F {
/*implicit*/ F(int t_) : t(t_) {}
bool operator==(F const& o) const { return t == o.t; }
private:
int t;
};
42 == F{42}
は以下のように評価されます。
F{42}.operator==(42)
と評価され、正常に動作します。
非メンバー候補を残したい場合は、その逆候補を明示的に追加すればよいでしょう。
struct F {
/*implicit*/ F(int t_) : t(t_) {}
bool operator==(F const& o) const { return t == o.t; }
bool operator==(int i) const { return t == i; }
friend bool operator==(const int& y, const F& x) { return x == y; }
private:
int t;
};
これは
42 == F{42}
は依然として非会員候補を選びますが、今度は
x == y
はメンバー候補を選びますが、本文ではメンバー候補を選び、通常の等式を行います。
この最後のバージョンは、非メンバー候補を削除することもできます。また、以下はすべてのテストケースで再帰なしで動作します (そして、私は今後 C++20 で比較をどのように記述するかということです)。
struct F {
/*implicit*/ F(int t_) : t(t_) {}
bool operator==(F const& o) const { return t == o.t; }
bool operator==(int i) const { return t == i; }
private:
int t;
};
関連
-
[解決済み] error: 'if' の前に unqualified-id を期待した。
-
[解決済み] 既に.objで定義されている-二重包含はない
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】オブジェクト引数のない非静的メンバ関数の呼び出し コンパイラーエラー
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み] [Solved] インクルードファイルが開けません。'stdio.h' - Visual Studio Community 2017 - C++ Error
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。
-
[解決済み】Enterキーを押して続行する
-
[解決済み】エラー。引数リストに一致するコンストラクタのインスタンスがない
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] [Solved] Error C1083: Cannot open include file: 'stdafx.h'
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み] string does not name a type Errorが発生するのはなぜですか?
-
[解決済み] クラスにデフォルトコンストラクタが存在しない。
-
[解決済み] 非常に基本的なC++プログラムの問題 - バイナリ式への無効なオペランド
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み] 配列のベクトルを扱う正しい方法
-
[解決済み】Eclipse IDEでC++エラー「nullptrはこのスコープで宣言されていません」が発生する件
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++
-
[解決済み】'std::cout'への未定義の参照