Search code examples
c++copy-constructormove-semanticsrvalue-referencereturn-value-optimization

Why copy constructor is called instead of move constructor?


please look at the following example code:

   #include <iostream>

    struct Foo {
        Foo() { std::cout << "Default!\n"; }
        Foo(const Foo& foo) { std::cout << "Copy!\n"; }
        Foo(Foo&& foo) { std::cout << "Move!\n"; }
    };

    struct Bar {
        Foo foo;
        Bar() {}
        Bar(Bar &that) : foo(that.foo) {}
        Bar(Bar &&that) : foo(std::move(that.foo)) {}
    };

    Bar f() {
        Bar bar;
        return bar;
    }

    int main() {
        Bar bar(f());
    }

I'am expecting that the output of this code should be:

Default!
Move!

but what I get is:

Default!
Copy!

I can't see any reason why the copy constructor is called instead of the move constructor. If I put the keyword const in front of Bar &that in the declaration of the copy constructor of struct Bar, I've got the right result. I know that it is better for many cases to take a const lvalue reference rather than just an lvalue reference for copy constructors, but I just want to know the reason why this happened.

Why in this example Bar & has been preferred over Bar && even though the return value of f() should be considered as a prvalue? Why the keyword const solves the problem? Does const really solve the problem? Does it have something to do with RVO (Return Value Optimization)? Or, is this just a compiler bug?

I've tested this example on Visual C++ November 2012 CTP.

I've found a similar issue here:

Copy constructor is being called instead of the move constructor

But I still can't understand why.

Can anyone help me?


Solution

  • Wow, when I compile this with...

    1. Visual Studio in Debug I see "Default! Copy!".
    2. Visual Studio in Release I see "Default!".
    3. If you change Bar(Bar &that) to Bar(const Bar &that) then "Default! Move!"
    4. Shockingly, if you switch the order of Bar(Bar &that) with Bar(Bar &&that) (so that the move ctor is defined first) then you'll actually see "Default! Move!"