7

私のようなクラスがある場合

class Foo{
public:
    Foo(){...}
    Foo(Foo && rhs){...}
    operator=(Foo rhs){ swap(*this, rhs);}
    void swap(Foo &rhs);
private:
    Foo(const Foo&);
// snip: swap code
};
void swap(Foo& lhs, Foo& rhs);

コピーコンストラクターがない場合は、operator = by valueとswapを実装するのは理にかなっていますか?クラスのオブジェクトのコピーを防ぐ必要Fooがありますが、移動は許可します。

このクラスはコピーできないので、構成をコピーしたり、コピーして割り当てたりすることはできません。

編集

私はこれで私のコードをテストしました、そしてそれは私が望む振る舞いをしているようです。

#include <utility>
#include <cstdlib>
using std::swap;
using std::move;
class Foo{
public: Foo():a(rand()),b(rand()) {}
        Foo(Foo && rhs):a(rhs.a), b(rhs.b){rhs.a=rhs.b=-1;}
        Foo& operator=(Foo rhs){swap(*this,rhs);return *this;}
        friend void swap(Foo& lhs, Foo& rhs){swap(lhs.a,rhs.a);swap(lhs.b,rhs.b);}
private:
    //My compiler doesn't yet implement deleted constructor
    Foo(const Foo&);
private:
    int a, b;
};

Foo make_foo()
{
    //This is potentially much more complicated
    return Foo();
}

int main(int, char*[])
{
    Foo f1;
    Foo f2 = make_foo(); //move-construct
    f1 = make_foo(); //move-assign
    f2 = move(f1);
    Foo f3(move(f2));
    f2 = f3; // fails, can't copy-assign, this is wanted
    Foo f4(f3); // fails can't copy-construct

    return 0;
}
4

3 に答える 3

6

移動と交換は確かに合理的です。コピーコンストラクターを無効にした場合、この関数を呼び出すことができる唯一の方法は、移動コンストラクターを使用して引数を作成する場合です。これは、あなたが書く場合

lhs = rhs; // Assume rhs is an rvalue

次に、引数toのコンストラクターはoperator =、moveコンストラクターで初期化され、rhs引数を空にして、の古い値に設定しますrhs。次に、の呼び出しは、の古い値との古い値をswap交換し、の古い値を保持したままにします。最後に、引数のデストラクタが起動し、の古いメモリがクリーンアップされます。注意として、これは実際には移動と交換ほどコピーと交換ではありません。lhsrhslhsrhslhs

とはいえ、あなたが今持っているものは正しくありません。のデフォルトの実装は、std::swap内部的にmoveコンストラクターを使用して要素を移動しようとします。これにより、無限の再帰ループが発生します。std::swapこれを正しく機能させるには、オーバーロードする必要があります。

あなたはここideoneでこれをオンラインで見ることができます。

詳細については、この質問と「4.5のルール」に関する説明を参照してください。

お役に立てれば!

于 2011-07-26T00:52:12.737 に答える
2

これは問題ないと思いますが、なぜあなたがそうしないのか、私にはよくわかりません。

operator=(Foo&& rhs) // pass by rvalue reference not value

そして、あなた自身の動きを保存します。

于 2011-07-26T02:21:56.200 に答える
1

以下は意見であり、私は実際には0x基準に達していませんが、私を支持するかなり確固たる理由があると思います。

いいえ。実際、割り当てをまったくサポートしないのが適切です。

セマンティクスを検討してください。

「割り当て」とは、「すでに存在するBをAと同一にする」ことを意味します。「コピー」とは、「Bを作成し、Aと同一にする」ことを意味します。「スワップ」とは、「BをAと同一にすると同時に、AをBと同一にする」ことを意味します。「移動」とは、「BをAと同一にし、Aを破壊する」という意味です。

コピーできない場合は、コピーアンドスワップできません。コピーアンドスワップは、代入を実装するための安全な方法です。Aと同じCを作成し、Bと交換して(CがBと同じになり、BがAと同じになるように)、Cを破棄します。 (古いBデータのクリーンアップ)。これは、移動と交換では機能しません。どの時点でもAを破棄してはなりませんが、移動するとAが破棄されます。さらに、移動しても新しい値は作成されないため、AをBに移動すると、交換するものは何もありません。

それに加えて、クラスをコピー不可にする理由は、「Bの作成」が問題になるためではなく、「Aと同一になるようにする」が問題になるためです。IOW、コピーできない場合、なぜ割り当てることができると期待する必要があるのでしょうか。

于 2011-07-26T02:40:00.570 に答える