web-dev-qa-db-ja.com

コンパイラによって生成された移動コンストラクタの動作は何ですか?

_std::is_move_constructible<T>::value == true_は、Tに使用可能な移動コンストラクターがあることを意味しますか?もしそうなら、それのデフォルトの動作は何ですか?

次の場合を考えてみましょう。

_struct foo {
    int* ptr;
};

int main() {
    {       
        std::cout << std::is_move_constructible<foo>::value << '\n';
        foo f;
        f.ptr = (int*)12;
        foo f2(std::move(f));
        std::cout << f.ptr << ' ' << f2.ptr << '\n';
    }
    return 0;
}
_

出力は次のとおりです。

_1
0000000C 0000000C
_

_f.ptr_はnullptrであるべきだと思いました。したがって、この場合、

  1. _f2_移動は構築されていますか?
  2. もしそうなら、右辺値を無効にすべきではありませんか?
  3. クラスのインスタンスを適切に移動構築できるかどうか(古いインスタンスを無効にする)を知るにはどうすればよいですか?

(私はVS11を使用しています。)

更新

移動コンストラクターのデフォルトの動作はコピーコンストラクターと同じですが、正しいですか?それが本当なら、

  1. 移動ctorが移動元オブジェクトのリソースを盗むことを常に期待していますが、デフォルトのオブジェクトは期待どおりに動作しません。デフォルトの移動ctorを使用する意味は何ですか?
  2. クラスにカスタム移動コンストラクター(適切に動作することが保証されている)があるかどうかをどのように知ることができますか?

宣言したときにfoo f2(std::move(f));がコピーctorを呼び出しているようです。以下を参照してください。

_struct foo {
    int* ptr;
    foo() {}
    foo(const foo& other) {
        std::cout << "copy constructed\n";
    }
};

int main() {
    {       
        std::cout << std::is_move_constructible<foo>::value << '\n';
        foo f;
        foo f2(std::move(f));
    }
    system("pause");
    return 0;
}
_

これで、出力は次のようになります。

_1
copy constructed
_

fooにmoveコンストラクターがある場合、foo f2(std::move(f))はそれを呼び出さないでしょうか?

だから今私の質問は次のとおりです:クラスにmove ctorがあるかどうかを知る方法、そしてある場合、明示的に呼び出すにはどうすればよいですか?

私がやろうとしているのは…

_template<typename T, bool has_move_ctor>
struct MoveAux;

template<typename T>
struct MoveAux<T, true> {
    static void doMove(T* dest, T* src) {
        new(dest) T(std::move(*src)); //move ctor
    }
};

template<typename T>
struct MoveAux<T, false> {
    static void doMove(T* dest, T* src) {
        new(dest) T(*src); //copy ctor
        src->~T();
    }
};

template<typename T>
inline doMove(T* dest, T* src) {
    MoveAux<T,/*a trait*/>::doMove(dest, src);
}
_

したがって、_std::is_move_constructible<T>::value_をテンプレートに渡すことができると思いましたが、この特性はT t(T())が有効な式である場合にのみ問題になり、T::T(const T&)を呼び出す可能性があることがわかりました。ここで、Tがカスタムクラスであると仮定すると、上記のテンプレートを次のように動作させる必要があります。

  1. Move ctorを宣言しない場合は、テンプレートメソッドが_MoveAux<T,false>::doMove_を呼び出すようにします。
  2. 宣言した場合は、_MoveAux<T,true>::doMove_を呼び出す必要があります。

これを機能させることは可能ですか?

34
Frahm

std::is_move_constructible<T>::value == trueは、Tに使用可能な移動コンストラクターがあることを意味しますか?

移動コンストラクターまたはコピーコンストラクターのいずれか。コピー構築の操作は、操作移動構築に課せられるすべての要件、およびその他の要件を満たしていることを忘れないでください。

標準用語では、MoveConstructibleオブジェクトは、式の評価の対象となるオブジェクトです。

T u = rv; 

uを構築前のrvの値と同等にします。 rvafterの移動元の状態はunspecified。ただし、指定されていないため、状態はrvbeforeから移動された状態と同じである可能性もあります。 、uは、rvcopyである可能性があります。

実際、標準では、CopyConstructibleの概念をMoveConstructibleの概念の絞り込みと定義しています(つまり、 CopyConstructibleMoveConstructibleですが、その逆はありません)。

もしそうなら、それのデフォルトの動作は何ですか?

暗黙的に生成されたmoveコンストラクターの動作は、生成されたタイプのデータメンバーのメンバーごとの移動を実行することです。

C++ 11標準のパラグラフ12.8/15によると:

非ユニオンクラスの暗黙的に定義されたコピー/移動コンストラクターXは、そのベースとメンバーのメンバーごとのコピー/移動を実行します。 [注:非静的データメンバーの中括弧または等しい初期化子は無視されます。 12.6.2の例も参照してください。 —エンドノート]

さらに:

1-はf2移動構築?

はい。

2-もしそうなら、右辺値を無効にすべきではありませんか?

ポインタを移動することは、それをコピーすることと同じです。したがって、無効化は行われていません。移動元オブジェクトを特定の状態のままにする(つまり、ポインターデータメンバーをnullptrに設定する)移動コンストラクターが必要な場合は、独自に作成する必要があります。または、この責任をスマートポインタークラスに委任する必要があります。なので std::unique_ptr

ここでは、「invalidated」という単語が正しくないことに注意してください。ムーブコンストラクター(およびムーブ代入演算子)は、moved-fromオブジェクトをvalid(まだ指定されていない)状態のままにすることを目的としています。

言い換えると、クラスの不変条件を尊重する必要があります。また、状態(通常は破棄と割り当て)に前提条件がないオブジェクトから移動した操作を呼び出すことができる必要があります。

22
Andy Prowl

std :: is_move_constructible :: value == trueは、Tに使用可能な移動コンストラクターがあることを意味しますか?

いいえ。それはあなたができると述べています オブジェクト型の右辺値式を取り、それからオブジェクトを構築する 。これがmoveコンストラクターを使用するかcopyコンストラクターを使用するかは、この特性とは関係ありません。

f2ムーブは構築されていますか?

はい。

もしそうなら、右辺値を無効にすべきではありませんか?

いいえ、それは動きがどのように機能するかではありません。

クラスのインスタンスが適切に移動構築できるかどうか(古いインスタンスを無効にする)をどのように知ることができますか?

それは、存在する「適切に動く-構築された」の定義ではありません。 「古いものを無効にする」場合は、自分で行う必要があります。

移動構造は通常、古いオブジェクトの状態についてnothingを保証します。有効ですが未定義の状態になります。そのような状態は「以前と同じ」である可能性が非常に高いです。ポインターの移動構造は、ポインターのコピーと同じです。

移動後に「無効化」する場合は、それを明示的に行う独自の移動コンストラクターを作成する必要があります。

(私はVS11を使用しています)

その場合、コンパイラによって生成された移動コンストラクタはまったくありません。ポインタのmoveコンストラクタとcopyコンストラクタはどちらも同じことをするので、それは問題ではありません。

5
Nicol Bolas

移動コンストラクターのデフォルトの動作はコピーコンストラクターと同じですが、正しいですか?それが本当なら

いいえ、それは間違っています。これはプリミティブにのみ当てはまります。コピーコンストラクターと似ています。

デフォルトで生成されたコピーコンストラクターは、宣言された順序ですべてのメンバーのcopyコンストラクターを呼び出します

ただし、デフォルトで生成されるmoveコンストラクターは、宣言された順序ですべてのメンバーのmoveコンストラクターを呼び出します。

次の質問は、プリミティブのコピー/移動コンストラクターints floats pointersは何をするのか?

回答:値をコピーするだけです(コピーコンストラクターと移動コンストラクターの両方)

3
balki

Visual Studio 2012/VC++ 11は、コンパイラーによって生成された移動コンストラクターをサポートしていませんサポートしていません。実際、この引用を "Visual C++ 11のC++ 11機能" ブログ投稿(私の強調)から検討してください。

右辺値参照v3.0は、特定の条件下でムーブコンストラクターとムーブ代入演算子を自動的に生成する新しいルールを追加します。このはVC11には実装されません。これは、VC10のの動作に従い、ムーブコンストラクター/ムーブを自動的に生成しません。代入演算子

rawポインタを使用すると、古い「moved-from」ポインタを手動でクリアして、移動コンストラクタを自分で定義する必要があります。

class Foo 
{
public:

    // Move constructor
    Foo(Foo&& other)
        : m_ptr(other.m_ptr) // copy pointer value
    {
        // Clear out old "moved-from" pointer, to avoid dangling references
        other.m_ptr = nullptr;
    }

private:
    int* m_ptr;
};

代わりに、std::unique_ptrのようなスマートポインターを使用する場合、移動コンストラクターは適切に定義され、std::moveを呼び出すことができます。

class Foo 
{
public:

    // Move constructor
    Foo(Foo&& other)
        : m_ptr(std::move(other.m_ptr)) // move from other, 
                                        // old pointer automatically cleared
    {
    }

private:
    std::unique_ptr<int> m_ptr;
};

自動生成移動コンストラクターを使用すると、member-wisemoveがOKの場合、カスタム移動コンストラクターを明示的に定義する必要はありません。あなたのために。

2
Mr.C64

fooにmoveコンストラクターがある場合、foo f2(std :: move(f))はそれを呼び出しませんか?コピーコンストラクターを指定すると、デフォルトの移動コンストラクターは取得されません。次の行を追加して取得します(変更に注意してください)。 foo(foo && ifm)= default;

0
qqqqq

n3376 12.8/15

非ユニオンクラスXの暗黙的に定義されたcopy/moveコンストラクターは、そのベースとメンバーのmemberwise copy/moveを実行します。

各ベースまたは非静的データメンバーは、そのタイプに適した方法でコピー/移動されます。

—メンバーが配列の場合、各要素はxの対応するサブオブジェクトで直接初期化されます。

—メンバーmの右辺値参照型がT &&の場合、static_cast(x.m);で直接初期化されます。

—それ以外の場合、ベースまたはメンバーは、対応するxのベースまたはメンバーで直接初期化されます。

0
ForEveR