web-dev-qa-db-ja.com

`= default`移動コンストラクターは、メンバーごとの移動コンストラクターと同等ですか?

これは

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB}               { }
    Example(const Example& mE) : a{mE.a}, b{mE.b}        { }
    Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
    Example& operator=(const Example& mE) { a = mE.a; b = mE.b; return *this; } 
    Example& operator=(Example&& mE)      { a = move(mE.a); b = move(mE.b); return *this; } 
}

これと同等

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB} { }
    Example(const Example& mE)            = default;
    Example(Example&& mE)                 = default;
    Example& operator=(const Example& mE) = default;
    Example& operator=(Example&& mE)      = default;
}

86
Vittorio Romeo

はい、両方とも同じです。

しかし

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB} { }
    Example(const Example& mE)            = default;
    Example(Example&& mE)                 = default;
    Example& operator=(const Example& mE) = default;
    Example& operator=(Example&& mE)      = default;
}

このバージョンでは、本文の定義をスキップできます。

ただし、explicitly-defaulted-functionsを宣言するときは、いくつかの規則に従う必要があります。

8.4.2明示的にデフォルト設定された関数[dcl.fct.def.default]

次の形式の関数定義:

  attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;

explicitly-defaulted定義と呼ばれます。明示的にデフォルト設定されている関数は

  • 特別なメンバー関数である、

  • 同じ宣言された関数型を持ちます(異なるref-qualifiersを除く)。ただし、コピーコンストラクターまたはコピー割り当て演算子の場合は、パラメーター型「非const Tへの参照」の場合があります。ここで、Tは、暗黙的に宣言されているかのように、メンバー関数のクラスの名前です。

  • デフォルト引数はありません。

44

はい、デフォルトの移動コンストラクターは、そのベースとメンバーのメンバーごとの移動を実行します。

Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }

以下と同等です:

Example(Example&& mE)                 = default;

ドラフトC++ 11標準 セクション12.8クラスオブジェクトのコピーと移動パラグラフ1これは(今後のエンファシスマイニング):

デフォルトであり、削除済みとして定義されていないコピー/移動コンストラクターが暗黙的に定義されているは、もし修正された場合(3.2)または最初の宣言後に明示的にデフォルトになった場合です。 [注:実装がそのodr-use(3.2、12.2)を省略した場合でも、コピー/移動コンストラクターは暗黙的に定義されます。 —注を終了] [...]

段落15は次のとおりです。

implicitly-defined copy/move constructornon-union class Xは、そのベースとメンバーのメンバーごとのコピー/移動を実行します。 [注:非静的データメンバーのbrace-or-equal-initializerは無視されます。 12.6.2の例を参照してください。 —end note]初期化の順序は、ユーザー定義コンストラクターのベースとメンバーの初期化の順序と同じです(12.6.2を参照)。 xをコンストラクターのパラメーター、または移動コンストラクターの場合は、パラメーターを参照するxvalueとします。各ベースまたは非静的データメンバは、そのタイプに適した方法でコピー/移動されます。

  • メンバーが配列の場合、各要素はxの対応するサブオブジェクトで直接初期化されます。
  • メンバーmに右辺値参照型T &&がある場合、static_cast(x.m)で直接初期化されます。
  • それ以外の場合、ベースまたはメンバーは、xの対応するベースまたはメンバーで直接初期化されます。

仮想基本クラスのサブオブジェクトは、暗黙的に定義されたコピー/移動コンストラクターによって一度だけ初期化されます(12.6.2を参照)。

19
Shafik Yaghmour

=default移動コンストラクターは、メンバーごとの移動コンストラクターと同等ですか?

はい更新:さて、常にではありません。この例を見てください:

#include <iostream>

struct nonmovable
{
    nonmovable() = default;

    nonmovable(const nonmovable  &) = default;
    nonmovable(      nonmovable &&) = delete;
};

struct movable
{
    movable() = default;

    movable(const movable  &) { std::cerr << "copy" << std::endl; }
    movable(      movable &&) { std::cerr << "move" << std::endl; }
};

struct has_nonmovable
{
    movable    a;
    nonmovable b;

    has_nonmovable() = default;

    has_nonmovable(const has_nonmovable  &) = default;
    has_nonmovable(      has_nonmovable &&) = default;
};

int main()
{
    has_nonmovable c;
    has_nonmovable d(std::move(c)); // prints copy
}

以下を印刷します:

copy

http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb

デフォルトの移動コンストラクターを宣言しましたが、移動ではなくコピーが発生します。どうして?クラスに単一の非可動メンバーさえある場合、explicitlydefaultedmove constructor is暗黙的にdeleted(このようなしゃれ)。したがって、has_nonmovable d = std::move(c)を実行すると、has_nonmovableの移動コンストラクターが(暗黙的に)削除されるため、コピーコンストラクターが実際に呼び出されます(式has_nonmovable(has_nonmovable &&) = default)。

ただし、non_movableの移動コンストラクターがまったく宣言されていない場合、移動コンストラクターはmovable(および移動コンストラクターを持つすべてのメンバー)に使用され、コピーコンストラクターはnonmovable(および移動コンストラクターを定義していないすべてのメンバーに対して)。例を参照してください。

#include <iostream>

struct nonmovable
{
    nonmovable() = default;

    nonmovable(const nonmovable  &) { std::cerr << "nonmovable::copy" << std::endl; }
    //nonmovable(      nonmovable &&) = delete;
};

struct movable
{
    movable() = default;

    movable(const movable  &) { std::cerr << "movable::copy" << std::endl; }
    movable(      movable &&) { std::cerr << "movable::move" << std::endl; }
};

struct has_nonmovable
{
    movable    a;
    nonmovable b;

    has_nonmovable() = default;

    has_nonmovable(const has_nonmovable  &) = default;
    has_nonmovable(      has_nonmovable &&) = default;
};

int main()
{
    has_nonmovable c;
    has_nonmovable d(std::move(c));
}

以下を印刷します:

movable::move
nonmovable::copy

http://coliru.stacked-crooked.com/a/420cc6c80ddac407

更新:しかし、has_nonmovable(has_nonmovable &&) = default;行をコメントアウトすると、両方のメンバーにコピーが使用されます: http: //coliru.stacked-crooked.com/a/171fd0ce335327cd -印刷:

movable::copy
nonmovable::copy

おそらく、=defaultをどこにでも置くことは、まだ理にかなっています。移動式が常に移動するという意味ではありませんが、この可能性が高くなります。

もう1つの更新:しかし、has_nonmovable(const has_nonmovable &) = default;行をコメントアウトすると、結果は次のようになります。

movable::move
nonmovable::copy

したがって、プログラムで何が起こるかを知りたい場合は、自分ですべてをしてください:ため息:

7
anton_rh