45

効果的な C++」項目 3 には、「可能な限り const を使用する」と記載されており、次のような例が示されています。

const Rational operator*(const Rational& lhs, 
                            const Rational& rhs);

クライアントがこのような残虐行為を犯すのを防ぐために:

Rational a, b, c;
...
(a * b) = c;   // invoke operator= on the result of a*b!

しかし、関数の非参照戻り値はすでに右辺値ではありませんか? では、なぜわざわざこれを行うのですか?

4

5 に答える 5

52

要点は、クラス型(組み込み型ではない) の場合、a = bは単に の省略形であるa.operator=(b)ということです。ここで、 operator= はメンバー関数です。(a * b)また、メンバー関数は、によって作成された右辺値で呼び出すことができますRational::operator*組み込みの右辺値 (" do as the ints do ") と同様のセマンティクスを強制するために、一部の作成者 (Meyers を含む) は、C++98 でそのような演算子を持つクラスの const-rvalue で返すことを推奨しました。

ただし、C++11 では、const 右辺値が にバインドできないため、移動セマンティクスが阻害されるため、const-rvalue によって戻ることは悪い考えT&&です。

Scott Meyers は、彼のノートAn overview of the new C++ (C++11)で、彼の古い本とまったく同じ例を示し、const 戻り値を追加するのは設計が不十分であると見なされていると結論付けています。推奨される署名は現在

Rational operator*(const Rational& lhs, const Rational& rhs);

更新:コメントで @JohannesSchaub-litb が暗示しているように、C++11 では、代入演算子で参照修飾子*thisを使用して、左辺値のみを左引数 (つまり、ポインター、これがこの機能の理由です)として受け入れるようにすることもできます。 「*this の右辺値参照」とも呼ばれます)。それを利用するには、g++ >= 4.8.1 (リリースされたばかり) または Clang >= 2.9 が必要です。

于 2013-05-30T11:30:24.760 に答える
5

おそらく、これは担当者のポイントを失うことになるでしょうが、私は同意しません. クラスのユーザーを困らせるため、オーバーロードされた演算子の予想される戻り値の型を変更しないでください。すなわち使用

Rational operator*(const Rational& lhs, const Rational& rhs);

(もちろん、パラメーターconstをing することは良い習慣であり、定数参照パラメーターを持つことは、コンパイラーがディープ コピーを取得しないことを意味するため、さらに優れています。ただし、この場合、定数参照の戻り値を持たないでください。壊滅的なダングリング参照. しかし、参照を取ることは値による受け渡しよりも遅い場合があることに注意してください. 多くのプラットフォームでs とs がそのカテゴリに入る.)doubleint

于 2013-05-30T11:30:15.467 に答える
3

代わりに書くことを意図している可能性があるため、(a * b) == cつまり

if ((a * b) = (c + d)) // executes if c+d is true

しかし、あなたはしたかった

if ((a * b) == (c + d)) // executes if they're equal
于 2013-05-30T11:29:56.657 に答える
1

あなたの質問によると、あなたがやりたいことは、対応する operator= private を宣言して、もうアクセスできないようにすることだと思います。

したがって、一致する署名をオーバーロードする必要があります(a*b) = c。左の部分は式であるため、右辺値の方が適切であることに同意します。ただし、関数をオーバーロードして右辺値を返す場合、これが関数の戻り値であるという事実を無視しています。オーバーロード規則は戻り値を考慮しないため、コンパイラは無効なオーバーロードについて文句を言います。

ここで述べたように、代入の演算子オーバーロードは常に内部クラス定義です。オーバーロード解決のような非メンバー署名がある場合はvoid operator=(foo assignee, const foo& assigner);、最初の部分を右辺値として一致させることができます (その後、それを削除するか、プライベートに宣言することができます)。

したがって、次の 2 つの世界から選択できます。

  • ユーザーは間違っていないような愚かなことを書くことができるという事実を(a*b) = c受け入れますが、c の値はアクセスできない一時的な
  • および犠牲移動セマンティクスを禁止する署名const foo operator*(const foo& lhs, const foo& rhs)を使用する(a*b) = c

コード

#include <utility>

class foo {
    public:
    foo() = default;
    foo(const foo& f) = default;
    foo operator*(const foo& rhs) const {
        foo tmp;
        return std::move(tmp);
    }
    foo operator=(const foo& op) const {
        return op;
    }
    private:
    // doesn't compile because overloading doesn't consider return values.
    // conflicts with foo operator=(const foo& op) const;
    foo && operator=(const foo& op) const; 
};


int main ( int argc, char **argv ) {
    foo t2,t1;
    foo t3 = t2*t1;
    foo t4;
    (t2 * t1) = t4;
    return 0;
}
 
于 2013-05-30T20:31:26.180 に答える