4

このコードを別の質問に投稿しましたが、新たな疑問があります:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class X
{
    public:

    std::vector<double> data;

    // Constructor1
    X():
        data(100000) // lots of data
    {
        cout << "X default constructor called";
    }

    // Constructor2
    X(X const& other): // copy constructor
        data(other.data)   // duplicate all that data
    {
        cout << "X copy constructor called";
    }

    // Constructor3
    X(X&& other):  // move constructor
        data(std::move(other.data)) // move the data: no copies
    {
        cout << "X move constructor called";
    }

    X& operator=(X const& other) // copy-assignment
    {
        cout << "X copy assignment called";
        data=other.data; // copy all the data
        return *this;
    }

    X& operator=(X && other) // move-assignment
    {
        cout << "X move assignment called";
        data=std::move(other.data); // move the data: no copies
        return *this;
    }

};

class X2
{
    public:

    std::vector<double> data;

    // Constructor1
    X2():
        data(100000) // lots of data
    {}

    // Constructor2
    X2(X const& other): // copy constructor
        data(other.data)   // duplicate all that data
    {}

    X2& operator=(X const& other) // copy-assignment
    {
        data=other.data; // copy all the data
        return *this;
    }
};

X make_x()
{
    X myNewObject; // Il normale costruttore viene chiamato qui
    myNewObject.data.push_back(22);
    return myNewObject; // Si crea un oggetto temporaneo prima di ritornare con il move constructor perchè myNewObject dev'essere distrutto
}


int main()
{
    X x1 = make_x(); // x1 has a move constructor


    X2 x2 = make_x(); // x2 hasn't a move constructor
}

main() 行では、move 割り当てと copy 割り当てが呼び出されることを期待しますが、そうではありません!

MSVC2012 の出力は次のとおりです。

X デフォルト コンストラクタと呼ばれる X ムーブ コンストラクタと呼ばれる X デフォルト コンストラクタと呼ばれる X ムーブ コンストラクタと呼ばれる

そしてg ++のものは

呼び出された X デフォルト コンストラクター 呼び出された X デフォルト コンストラクター

http://liveworkspace.org/code/220erd $2

課題はどこですか?? 最初の main() 行は移動代入を呼び出し、2 番目の main() 行はコピー代入を呼び出すと思いました

4

2 に答える 2

8
// Constructor2
X2(X const& other): // copy constructor
    data(other.data)   // duplicate all that data
{}

X2& operator=(X const& other) // copy-assignment
{
    data=other.data; // copy all the data
    return *this;
}

まず第一に、これらはX2type の引数を取るため、 のコピー コンストラクターおよびコピー代入演算子ではありませんX。最初のものは、 から に変換できるため、実際には変換コンストラクターとして知られていXますX2

int x = 5;

これは に割り当てられている 5 ではありませんx。でx初期化されています5。初期化は、似ているように見えますが、代入と同じではありません。実際、コードでは代入はまったく発生しないため、移動代入演算子またはコピー代入演算子は使用されません。

あなたが与えた各コンパイラが実際に何をしているのかを見ることができます:

  1. MSVC

    まず、myNewObjectで作成されmake_xます。これは出力されX default constructor calledます。次に、return myNewObject;最初に戻り値へのコピーを移動として扱い、移動コンストラクターがあることを見つけて、それを呼び出します。

    コピー操作の省略の基準が満たされているか、ソース オブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値によって指定されているという事実を除いて満たされる場合、コピーのコンストラクターを選択するためのオーバーロードの解決は次のとおりです。オブジェクトが右辺値によって指定されたかのように最初に実行されます。

    戻り値は にコピーされx1ます。X copy constructor calledただし、出力が表示されないため、このコピーは明らかに省略されています。

    参照 (12.2) にバインドされていない一時クラス オブジェクトが同じ cv 非修飾型のクラス オブジェクトにコピー/移動される場合、一時オブジェクトをターゲットに直接構築することにより、コピー/移動操作を省略できます。省略されたコピー/移動の

    次に、myNewObjectへの 2 回目の呼び出しで別のオブジェクトが作成されmake_xます。これは再び出力されX default constructor calledます。その後、 を行うと同じ動きが起こりreturn myNewObjectます。を受け取るコンストラクタはx2出力を行わないため、戻り値からの構築は何Xも出力しません。

  2. GCC

    まず、MSVC と同様に でmyNewObject作成されます。make_xこれは出力されX default constructor calledます。

    GCC は、MSVC が行っていない追加の最適化を行うようになりました。myNewObjectから戻り値に移動するのではなく、戻り値の場所に直接構築するだけでよいことを認識しています。

    クラスの戻り値の型を持つ関数の return ステートメントで、式が関数の戻り値の型と同じ cvunqualified 型を持つ不揮発性自動オブジェクト (関数または catch 句パラメーター以外) の名前である場合、コピー/move 操作は、自動オブジェクトを関数の戻り値に直接構築することで省略できます

    x1次に、 MSVC の場合と同様に、一時オブジェクトからの構築によって引き起こされるのと同じ省略が実行されます。

    の 2 番目の呼び出しmake_xは、最初の呼び出しとまったく同じ方法でx2行われますが、X. もちろん、これは何も出力しません。

于 2013-04-02T18:21:43.600 に答える
5

名前付き戻り値の最適化、NRVOの効果が見られます。次のコード

X f()
{
  X tmp;
  // ...
  return tmp;
}

一時を完全に削除し、関数呼び出し元の戻りスロットに構築できます。あなたの例では、効果は関数がインライン化されx1x2直接構築されるようなものです。

割り当てを表示したい場合は、次のように記述できます。

X x1;
x1 = make_x();
于 2013-04-02T18:20:22.690 に答える