2017-02-05 4 views
29

削除された移動セマンティクス(ケース1)のタイプのフィールドを含むが、そのようなクラス(ケース2)のインスタンスでは使用できないクラスに対して、std::moveを使用することが許可されるのはなぜですか?C++では、移動操作が削除されたオブジェクトを含むクラスを移動できるのはなぜですか?

私はケース2を理解しています。移動コンストラクタを明示的に削除しているため、移動しようとするとエラーが発生します。しかし、私は、このクラスも移動されているケース1の場合にも当てはまると考えています。

class TestNonMovable { 
    std::string ss; 
public: 
    TestNonMovable(){} 
    TestNonMovable(const TestNonMovable&) {} 
    TestNonMovable(TestNonMovable&&) = delete; 
}; 

class SomeClass { 
    TestNonMovable tnm; 
}; 

int main() {  
    // case1: This compiles, my understanding is that for SomeClass::tnm compiler will use copy constrctor 
    SomeClass sc1; 
    SomeClass sc2 = std::move(sc1); 

    // case2: This does not compile, move constructor is explicitly deleted, compiler will not try using copy constructor 
    TestNonMovable tnm; 
    TestNonMovable tnm2 = std::move(tnm); //error: use of deleted function 'TestNonMovable::TestNonMovable(TestNonMovable&&)' 
} 
+0

関連:http://stackoverflow.com/q/20608662/1639256 – Oktalist

答えて

45

2つのクラスの違いに注意してください。 TestNonMovable(ケース2)の場合は、と指定すると、移動コンストラクタはdeleteと宣言されます。 TestNonMovable tnm2 = std::move(tnm);を使用すると、削除された移動コンストラクタが過負荷解決で選択され、エラーが発生します。

SomeClass(ケース1)では、移動コンストラクタとコピーコンストラクタを明示的に宣言しません。コピー・コンストラクターは暗黙的に宣言され、定義されますが、移動可能なデータ・メンバーがあるため、ムーブ・コンストラクターは暗黙的に宣言され、削除済みとして定義されます。 deleted implicitly declared move constructorは、過負荷の解決には参加しません。 SomeClass sc2 = std::move(sc1);を使用すると、コピーコンストラクタが選択され、コードが正常に動作します。 std::moveから返されたrvalueの参照は、lvalue-constのconst(つまりconst SomeClass&)にもバインドできます。

削除された暗黙的に宣言された移動コンストラクタは、オーバーロード解決によって無視されます(そうしないと、コピー初期化がrvalueから防止されます)。 (C++ 14以来)

BTW:std::moveそれ自体は両方とも許されます。 移動操作は実行されません。引数をrvalueに変換するだけです。移動操作は、どちらの場合でも構築時に行われます。

+1

あなたのお見積もりに「since C++ 14」と書かれていますが、質問にはC++ 11のタグが付けられています。答えはどちらの場合にも当てはまりますか? –

+12

@CarstenSこの変更は[欠陥の解決](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1402)によって導入されたため、実装はこれを適用する権利の範囲内にありますClangとGCCの両方が行うC++ 11モードのルール。 – Oktalist

関連する問題