334

私は、コンパイラに可能な限り多くの作業を任せることが大好きです。単純なクラスを作成する場合、コンパイラは「無料」で次のものを提供できます。

  • デフォルト (空の) コンストラクター
  • コピー コンストラクター
  • デストラクタ
  • 代入演算子 ( operator=)

operator==しかし、 orなどの比較演算子を提供するようには見えませんoperator!=。例えば:

class foo
{
public:
    std::string str_;
    int n_;
};

foo f1;        // Works
foo f2(f1);    // Works
foo f3;
f3 = f2;       // Works

if (f3 == f2)  // Fails
{ }

if (f3 != f2)  // Fails
{ }

これには正当な理由がありますか?メンバーごとの比較を実行することが問題になるのはなぜですか? 明らかに、クラスがメモリを割り当てる場合は注意が必要ですが、単純なクラスの場合、コンパイラは確かにこれを行うことができますか?

4

13 に答える 13

333

コンパイラがデフォルトのコピーコンストラクタを提供できる場合、同様のデフォルトを提供できるはずであるという議論operator==()は、ある程度理にかなっています。この演算子にコンパイラ生成のデフォルトを提供しないという決定の理由は、Stroustrup が「C++ の設計と進化」(セクション 11.4.1 - コピーの制御) でデフォルトのコピー コンストラクターについて述べたことから推測できると思います。 :

私は個人的に、コピー操作がデフォルトで定義されていることを残念に思っており、多くのクラスのオブジェクトのコピーを禁止しています。ただし、C++ はデフォルトの代入コンストラクターとコピー コンストラクターを C から継承しており、頻繁に使用されます。

したがって、「なぜ C++ にはデフォルトがないのですか?」ではなく、「なぜoperator==()C++ にはデフォルトの代入コンストラクターとコピー コンストラクターがあるのですか?」という質問が必要でした。 (おそらく C++ の問題のほとんどの原因ですが、おそらく C++ の人気の主な理由でもあります)。

私自身の目的のために、IDE で新しいクラスに使用するスニペットには、プライベート代入演算子とコピー コンストラクターの宣言が含まれているため、新しいクラスを生成するときに、デフォルトの代入操作とコピー操作が取得されません。宣言を明示的に削除する必要があります。private:コンパイラでそれらを生成できるようにしたい場合は、セクションからそれらの操作の。

于 2008-10-20T14:53:20.657 に答える
115

C++20 でも、コンパイラは暗黙的に生成operator==しません。

struct foo
{
    std::string str;
    int n;
};

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ill-formed

ただし、C++20 以降では、明示的にデフォルト設定できるようになります。==

struct foo
{
    std::string str;
    int n;

    // either member form
    bool operator==(foo const&) const = default;
    // ... or friend form
    friend bool operator==(foo const&, foo const&) = default;
};

デフォルト設定はメンバー==ごと==に行われます (既定のコピー コンストラクターがメンバーごとのコピー構築を行うのと同じ方法で)。==新しい規則は、との間の予想される関係も提供し!=ます。たとえば、上記の宣言では、次の両方を記述できます。

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ok!
assert(foo{"Anton", 1} != foo{"Anton", 2}); // ok!

この特定の機能 (デフォルト設定ととoperator==の間の対称性) は、より広範な言語機能である の一部であった1 つの提案に由来します。==!=operator<=>

于 2015-01-08T10:23:14.293 に答える
79

コンパイラーは、ポインター比較が必要なのか、深い (内部) 比較が必要なのかを知りません。

それを実装せず、プログラマーに自分で実装させる方が安全です。その後、彼らは好きなすべての仮定を立てることができます。

于 2008-10-20T09:53:07.190 に答える
45

私見、「正当な」理由はありません。この設計上の決定に同意する人が非常に多い理由は、彼らが値ベースのセマンティクスの力を習得することを学んでいないためです。実装で生のポインターを使用するため、多くのカスタム コピー コンストラクター、比較演算子、およびデストラクターを記述する必要があります。

適切なスマート ポインター (std::shared_ptr など) を使用する場合、通常は既定のコピー コンストラクターで問題なく、仮想的な既定の比較演算子の明らかな実装でも問題ありません。

于 2009-04-03T17:05:05.167 に答える
42

Cがそうしなかったので、C++は==をしなかったと答えました.Cがデフォルトの=のみを提供し、最初に==を提供しない理由は次のとおりです。C はシンプルに保ちたいと考えていました。C は = by memcpy で実装しました。ただし、パディングのために memcmp で == を実装することはできません。パディングが初期化されていないため、memcmp は同じでも異なると言います。空のクラスにも同じ問題が存在します。memcmp は、空のクラスのサイズがゼロではないため、それらが異なると言います。上記から、== の実装は C での = の実装よりも複雑であることがわかります。これに関するいくつかのコード例。私が間違っている場合は、訂正していただければ幸いです。

于 2011-12-07T16:50:56.110 に答える
22

C++20 は、デフォルトの比較演算子を簡単に実装する方法を提供します。

cppreference.com の例:

class Point {
    int x;
    int y;
public:
    auto operator<=>(const Point&) const = default;
    // ... non-comparison functions ...
};

// compiler implicitly declares operator== and all four relational operators work
Point pt1, pt2;
if (pt1 == pt2) { /*...*/ } // ok, calls implicit Point::operator==
std::set<Point> s; // ok
s.insert(pt1); // ok
if (pt1 <= pt2) { /*...*/ } // ok, makes only a single call to Point::operator<=>
于 2018-05-15T08:11:51.827 に答える
16

default を定義することはできませんが、通常は自分で==定義する default!=を定義でき==ます。このためには、次のことを行う必要があります。

#include <utility>
using namespace std::rel_ops;
...

class FooClass
{
public:
  bool operator== (const FooClass& other) const {
  // ...
  }
};

詳細については、 http://www.cplusplus.com/reference/std/utility/rel_ops/を参照してください。

さらに、 を定義するoperator< と、 <=、>、>= の演算子は、 を使用するときにそれから推測できますstd::rel_ops

std::rel_opsただし、予期しない型に対して比較演算子が推測される可能性があるため、使用する場合は注意が必要です。

基本的な演算子から関連する演算子を推測するより好ましい方法は、boost::operatorsを使用することです。

ブーストで使用されるアプローチは、スコープ内のすべてのクラスではなく、必要なクラスのみに対して演算子の使用を定義するため、より優れています。

また、「+=」から「+」、「-=」から - などを生成することもできます (完全なリストはこちら) 。

于 2008-10-20T11:23:28.613 に答える
11

C++0xにはデフォルト関数の提案があったので、これらdefault operator==; を明示的にすると役立つことがわかりました。

于 2008-10-20T09:59:31.607 に答える
5

概念的に、平等を定義することは容易ではありません。POD データの場合でも、フィールドが同じであっても、(異なるアドレスにある) 別のオブジェクトであっても、必ずしも等しいとは限りません。これは、実際にはオペレーターの使用法に依存します。残念ながら、あなたのコンパイラはサイキックではなく、それを推測することはできません。

これに加えて、デフォルトの機能は自分自身を撃つための優れた方法です。あなたが説明するデフォルトは、基本的にPOD構造体との互換性を保つためにあります。ただし、開発者がそれらを忘れたり、デフォルトの実装のセマンティクスを忘れたりすることで、十分な混乱を引き起こします。

于 2008-10-20T10:06:15.447 に答える
4

時間が経過してもこの質問への回答が完全なままになるように: C++20以降、コマンドで自動的に生成できますauto operator<=>(const foo&) const = default;

==、!=、<、<=、>、および >= のすべての演算子が生成されます。詳細については、 https://en.cppreference.com/w/cpp/language/default_comparisonsを参照してください。

オペレーターの見た目<=>から、宇宙船オペレーターと呼ばれています。C++ で宇宙船 <=> 演算子が必要な理由も参照してください。.

EDIT:C++ 11でも、それのかなりきちんとした代替品が利用可能std::tieです。動作するように変更された興味深い部分は次のとおりです。bool operator<(…)==

#include <tuple>

struct S {
………
bool operator==(const S& rhs) const
    {
        // compares n to rhs.n,
        // then s to rhs.s,
        // then d to rhs.d
        return std::tie(n, s, d) == std::tie(rhs.n, rhs.s, rhs.d);
    }
};

std::tieすべての比較演算子で動作し、コンパイラによって完全に最適化されます。

于 2019-09-06T12:29:07.837 に答える
2

これには正当な理由がありますか?メンバーごとの比較を実行することが問題になるのはなぜですか?

機能的には問題にならないかもしれませんが、パフォーマンスに関しては、デフォルトのメンバーごとの比較は、デフォルトのメンバーごとの割り当て/コピーよりも最適ではない可能性があります。代入の順序とは異なり、比較の順序はパフォーマンスに影響を与えます。これは、最初のメンバーが等しくない場合、残りはスキップできることを意味するためです。そのため、通常等しいメンバーがいくつかある場合、それらを最後に比較する必要があり、コンパイラーはどのメンバーが等しい可能性が高いかを知りません。

この例を考えてみましょう。verboseDescriptionは、可能な天気の説明の比較的小さなセットから選択された長い文字列です。

class LocalWeatherRecord {
    std::string verboseDescription;
    std::tm date;
    bool operator==(const LocalWeatherRecord& other){
        return date==other.date
            && verboseDescription==other.verboseDescription;
    // The above makes a lot more sense than
     // return verboseDescription==other.verboseDescription
     //     && date==other.date;
    // because some verboseDescriptions are liable to be same/similar
    }
}

(もちろん、コンパイラーは、比較に副作用がないことを認識した場合、比較の順序を無視する権利がありますが、おそらく、独自のより良い情報がない場合でも、ソースコードからそのキューを取得します。)

于 2015-12-04T20:01:33.843 に答える
0

私は、POD 型クラスの場合、コンパイラがそれを行うことができることに同意します。ただし、コンパイラが単純だと考えるかもしれないことは間違っているかもしれません。したがって、プログラマーに任せたほうがよいでしょう。

2 つのフィールドが一意である POD ケースが一度ありました。そのため、比較が真と見なされることはありません。ただし、必要な比較はペイロードでのみ比較する必要がありました。これは、コンパイラーが決して理解できないか、独自に理解できるものです。

その上、彼らは書くのに時間がかかりませんよね?!

于 2008-10-20T09:50:35.680 に答える