c++c++11move-semanticsmove-constructorstdmove

Is move constructor called twice in C++?


Look at this code:

class Foo
{
public:

    string name;

    Foo(string n) : name{n}
    {
        cout << "CTOR (" << name << ")" << endl;
    }

    Foo(Foo&& moved)
    {
        cout << "MOVE CTOR (moving " << moved.name << " into -> " << name << ")" << endl;

        name = moved.name + " ###";
    }

    ~Foo()
    {
        cout << "DTOR of " << name << endl;
    }
};

Foo f()
{
    return Foo("Hello");
}

int main()
{
    Foo myObject = f();

    cout << endl << endl;
    cout << "NOW myObject IS EQUAL TO: " << myObject.name;
    cout << endl << endl;

    return 0;
}

The output is:

[1] CTOR (Hello)

[2] MOVE CTOR (moving Hello into -> )

[3] DTOR of Hello

[4] MOVE CTOR (moving Hello ### into -> )

[5] DTOR of Hello ###

[6] NOW two IS EQUAL TO: Hello ### ###

[7] DTOR of Hello ### ###

Important note: I have disabled the copy elision optimization using -fno-elide-constructors for testing purposes.

The function f() constructs a temporary [1] and returns it calling the move constructor to "move" the resources from that temporary to myObject [2] (additionally, it adds 3 # symbols).

Eventually, the temporary is destructed [3].


I now expect myObject to be fully constructed and its name attribute to be Hello ###.

Instead, the move constructor gets called AGAIN, so I'm left with Hello ### ###


Solution

  • The two move constructor calls are:

    1. Move the temporary created by Foo("Hello") into the return value.
    2. Move the temporary returned by the f() call into myObject.

    If you used a braced-init-list to construct the return value, there would only be a single move construction:

    Foo f()
    {
        return {"Hello"};
    }
    

    This outputs:

    CTOR (Hello)
    MOVE CTOR (moving Hello into -> )
    DTOR of Hello    
    NOW myObject IS EQUAL TO: Hello ###    
    DTOR of Hello ###
    

    Live Demo