0

私は何か間違ったことをしていますか?

#include <iostream>
using std::cout;

struct Map
{
    Map()
    {
        cout << "Map()\n";
    }
    Map(const Map& pattern)
    {
        cout << "Map(const Map& pattern)\n";
    }
    Map(Map&& tmp)
    {
        cout << "Map(Map&& tmp)\n";
    }
};

Map createMap()
{
    return Map();
}

int main(int argc, char* argv[])
{
    //dflt
    Map m;
    //cpy
    Map m1(m);
    //move
    Map m2(Map(m1));//<<I thought that I create here tmp unnamed obj.
    Map m3(createMap());//<<---or at least here, but nope...
    return 0;
}

コード内のコメント行を参照してください

編集済み[FredOverflowの回答から取得]

int main() 
{ 
    std::cout << "default\n"; 
    Map m; 

    std::cout << "\ncopy\n"; 
    Map m1(m); 

    std::cout << "\nmove\n";
    Map m2((Map(m1))); 

    std::cout << "\nmove\n"; 
    Map m3(createMap()); 
}  

私は出力を得ています:

default
Map()

copy
Map(const Map& pattern)

move
Map(const Map& pattern)//Here why not move ctor aswell as copy?

move
Map()
Map()
Map(Map&& tmp)
4

5 に答える 5

4
Map m3(createMap());//<<---or at least here, but nope...

戻り値の最適化が実際に行われています。C++ では、コンパイラは、返されたオブジェクトのコピーを最適化し、結果が格納されている呼び出し元のオブジェクトを関数が直接操作できるようにすることができます。移動コンストラクターを呼び出す必要もありません。

関数をより複雑にして、コンパイラが最適化を使用できないようにします。そうすれば、実際に動くことがわかります。例えば:

Map createMap()
{
    Map a, b;
    if (rand())
        return a;
    return b;
}
于 2010-10-30T15:55:45.637 に答える
3

オブジェクトではなく、関数を宣言しています:

T name (T(blah));

以下と同等です。

T name(T blah);

これは関数宣言として認識できます。追加の括弧を使用できます:

Map m2 ((Map(m1)));

これは、最も厄介な解析と呼ばれます。

于 2010-10-30T15:32:32.790 に答える
1

このプログラムは、予想される出力を示します。

#include <iostream>
using std::cout;

struct Map
{
    Map()
    {
        cout << "Map()\n";
    }
    Map(const Map& pattern)
    {
        cout << "Map(const Map&)\n";
    }
    Map(Map&& tmp)
    {
        cout << "Map(Map&&)\n";
    }
};

Map createMap()
{
    Map m;
    return m;
}

int main(int argc, char* argv[])
{
    //dflt
    Map m;
    //cpy
    Map m1(m);
    //move
    Map m2 = createMap();//<<I thought that I create here tmp unnamed obj.
    std::cin.get();
    return 0;
}

createMap() の変更に注意してください。直接の一時変数は使用しませんが、名前付きの戻り値を使用します。このプログラムは、Visual Studio 2010 での意図した出力を示しています。

于 2010-10-30T16:20:42.540 に答える
1

main出力をよりよく理解するために、ルーチンをわずかに変更しました。

int main()
{
    std::cout << "default\n";
    Map m;

    std::cout << "\ncopy\n";
    Map m1(m);

    std::cout << "\nmove\n";
    Map m2(Map(m1));

    std::cout << "\nmove\n";
    Map m3(createMap());
}

そして、ここに出力がありますg++ -fno-elide-constructors

default
Map()

copy
Map(const Map& pattern)

move

move
Map()
Map(Map&& tmp)
Map(Map&& tmp)

他の人がすでに指摘しているように、Map m2(Map(m1));実際には関数宣言であるため、出力は得られません。2 番目の移動は、型名ではないため、関数宣言として解釈されません。createMapここには 2 つのムーブ コンストラクタが含まれます。1 つは、評価によって作成さMap()れた一時オブジェクトを、評価によって作成された一時オブジェクトに移動し、2 番目の移動では、後者からcreateMap()初期化します。m3これはまさに期待通りです。

最初の移動を書き込んで修正するMap m2((Map(m1)));と、出力は次のようになります。

move
Map(const Map& pattern)
Map(Map&& tmp)

繰り返しますが、驚くことではありません。を評価することによってコピー コンストラクターが呼び出されMap(m1)、その一時オブジェクトが に移動されm2ます。なしでコンパイルする-fno-elide-constructorsと、RVO や NRVO などのさらに効率的な最適化に置き換えられるため、移動操作は表示されなくなります。

default
Map()

copy
Map(const Map& pattern)

move
Map(const Map& pattern)

move
Map()

-fno-elide-constructorsVisual C++ には、移動のセマンティクスをよりよく理解するために使用できるものと同様のコンパイラ オプションがあると確信しています。

于 2010-10-30T16:01:32.610 に答える
0
Map createMap()
{
    return Map();
}

コンパイラーは上記で RVO (戻り値の最適化) を行ったので、一時的なものは作成されなかったと思います。

次のように変更すると、move ctor が呼び出されるのがわかります。

Map createMap()
{
    Map m;
    m.DoSomething(); // this should make the compiler stop doing RVO
    return m;
}
  • 一部のコンパイラは、コンパイラの設定 (デバッグ/リリース モード) に関係なく RVO を実行します (例: bcc32)。VCも同じだと思います。
于 2010-11-01T10:56:56.017 に答える