web-dev-qa-db-ja.com

C ++ 11移動コンストラクター


次のクラスを考慮して、moveコンストラクターを実装する正しい方法は何でしょうか。

class C {
public:
    C();
    C(C&& c);
private:
    std::string string;
}

もちろん、stringをコピーしたり2回割り当てを解除したりすることは避けてください。
基本的な例はわかりやすくするためのものであり、moveコンストラクターが必要であると仮定しましょう。


私は試した:

C::C(C&& c) {
    //move ctor
    string = std::move(c.string);
}

そして

C::C(C&& c) : string(std::move(c.string)) {
    //move ctor
}

どちらもgcc4.8で正常にコンパイルされ、正常に実行されます。 らしいオプションAは正しい動作であり、オプションBで移動する代わりにstringがコピーされます。
これは移動コンストラクターの正しい実装ですか?

26
unexpectedvalue

std::string自体にはmove-ctorがあり、Cに対して暗黙的に定義されたmove-ctorが適切な移動操作を処理します。自分で定義することはできません。ただし、他のデータメンバーがある場合、具体的には次のようになります。

12.8クラスオブジェクトのコピーと移動

12暗黙的に宣言されたコピー/移動コンストラクターは、そのクラスのインラインパブリックメンバーです。クラスXのデフォルトのcopy-/moveコンストラクターは、Xに次の場合、削除済み(8.4.3)として定義されます。

—自明ではない対応するコンストラクターを持つバリアントメンバーであり、Xはユニオンのようなクラスです。

— Mの対応するコンストラクターに適用されるオーバーロード解決(13.3)により、あいまいさや関数が削除されたり、からアクセスできなくなったりするため、コピー/移動できないクラスタイプM(またはその配列)の非静的データメンバーデフォルトのコンストラクタ、または

— Bの対応するコンストラクターに適用されるオーバーロード解決(13.3)により、デフォルトのコンストラクターから削除またはアクセスできないあいまいさまたは関数が発生するため、コピー/移動できない直接または仮想の基本クラスB、または

—移動コンストラクターの場合、非静的データメンバー、または移動コンストラクターがなく、簡単にコピーできない型の直接または仮想基本クラス。

1クラスXのコピー/移動コンストラクターは、ユーザーが提供も削除もしていない場合、および

—クラスXには、仮想関数(10.3)と仮想基本クラス(10.1)がなく、関数(10.3)と仮想基本クラス(10.1)がありません。

—各直接基本クラスサブオブジェクトをコピー/移動するために選択されたコンストラクターは簡単であり、

—クラスタイプ(またはその配列)であるXの非静的データメンバーごとに、そのメンバーをコピー/移動するために選択されたコンストラクターは簡単です。それ以外の場合、コピー/移動コンストラクターは重要です。

独自のmove-ctorを実装することをお勧めします。

Move-ctorが必要な場合は、初期化リスト構文をお勧めします。常に!そうしないと、初期化子リストに記載されていないオブジェクトごとのデフォルトの構造になってしまう可能性があります(これは、デフォルト以外のctorを持つメンバーオブジェクトに対してのみ強制されるものです)。

15
dirkgently

両方のバリアントが文字列を移動します。 2番目のバリアントは、後で移動して割り当てるためだけに空の文字列をデフォルトで作成しないため、優先する必要があります。

テストケースを確認してから、コンパイラのBugzillaリストを確認してください。両方のケースで確実に移動するようにする場合は、両方のstring::operator=(string&&)(1番目のケース)およびstring::string(string&&)(2番目のケース)への呼び出しをトレースする必要があります。

両方のコンストラクターが機能するはずです。したがって、どちらも正しい移動コンストラクターです。最初のものはデフォルトでstringを構築して割り当てるだけなので、2番目のものはより効率的かもしれませんが、2番目のものは単に移動して構築するため、より効率的です。 2番目の方法の効率が低い場合は、コンパイラのバグ(現在のコンパイラではC++ 11のサポートがまだ完了していないことを忘れないでください)またはテスト方法の欠陥(コピーと移動をどの程度正確にテストし、移動コンストラクタを確認しますか)が疑われます。どちらの場合も、割り当てopは呼び出されませんか?).

もちろん、可能であればいつでも、コンパイラーにC(C&&) = default;を介してコンストラクターを生成させることができます。

3
Grizzly

メモリを手動で管理する必要がないため、ここで移動コンストラクタを実装する必要はありません。移動コンストラクターは、クラスで動的配列を手動で使用する場合にのみ役立ちます。

要求しなくても、既に実行されているはずのデフォルトの移動コンストラクターをコンパイラーに明示的に作成させることができます。

C(C&& c) = default;
2
Morwenn