web-dev-qa-db-ja.com

友だちとして宣言する必要のあるオペレーターは?

一部の書籍やインターネットの多くで、「operator==は友達として宣言する必要があります」のような推奨事項が見られます。

演算子をフレンドとして宣言する必要がある場合と、メンバーとして宣言する必要がある場合を理解するにはどうすればよいですか? ==<<以外に、フレンドとして宣言する必要が最も多い演算子は何ですか?

34

これは、クラスがoperator==(または他の演算子)の呼び出しの左側にあるか右側にあるかに依存します。クラスが式の右側にあり、左側と比較できる型への暗黙の変換を提供しない場合は、operator==を別個に実装する必要があります。関数またはクラスのfriendとして。演算子がプライベートクラスのデータにアクセスする必要がある場合は、演算子mustfriendとして宣言します。

例えば、

class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
};

メッセージを文字列と比較できます

Message message("Test");
std::string msg("Test");
if (message == msg) {
    // do stuff...
}

しかし、その逆ではありません

    if (msg == message) { // this won't compile

クラス内でフレンドoperator==を宣言する必要があります

class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
    friend bool operator==(const std::string& lhs, const Message& rhs);
};

or暗黙の変換演算子を適切な型に宣言します

class Message {
    std::string content;
public:
    Message(const std::string& str);
    bool operator==(const std::string& rhs) const;
    operator std::string() const;
};

or個別の関数を宣言します。プライベートクラスデータにアクセスしない場合、フレンドである必要はありません。

bool operator==(const std::string& lhs, const Message& rhs);
43
Chris Frederick

クラス外に演算子がある場合、両方のパラメーターが暗黙の型変換に参加できます(一方、クラス本体で演算子が定義されている場合は、右側のオペランドのみが可能です)。一般的に、これはすべての古典的な2項演算子(つまり、==!=+-<<、...)。

もちろん、クラスの演算子friendsを宣言する必要があるのは、クラスのパブリックメンバーのみに基づいて結果を計算する必要がある場合とそうでない場合だけです。

19

一般に、操作するクラスのプライベートデータまたは保護されたデータに本当にアクセスする必要があるフリー関数として実装されている演算子のみをフレンドとして宣言する必要があります。

一般に、メンバー関数として実装する唯一の演算子は、基本的に非対称であり、オペランドに同等の役割がない演算子です。メンバーとして実装する傾向があるものは、メンバーになる必要があるものです:単純な割り当て、()[]および->と複合代入演算子、単項演算子、およびおそらくストリームまたはストリームのようなクラスの<<>>のオーバーロードクラス。 &&||、または,をオーバーロードすることはありません。

私がフリー関数として実装する他のすべてのオペレーターは、できれば操作するクラスのパブリックインターフェースを使用して、必要な場合にのみフレンドになるようにフォールバックします。

!===<+/などの演算子を非メンバー関数として保持すると、暗黙の変換シーケンスに関して左と右のオペランドを同じように処理できるため、驚くべき非対称の数を減らすことができます。

8
CB Bailey