Junekey Jeon
Junekey Jeon

Reputation: 1577

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?

Upvotes: 8

Views: 4815

Answers (3)

Ben Voigt
Ben Voigt

Reputation: 283644

It's just the usual non-compliance of Visual C++ allowing binding a non-const lvalue reference to a temporary. It violates the language rules, but it went too long before being caught, so now there's code that depends on the bug that would break if it were properly fixed.

That behavior mistakenly allowing the non-const copy constructor to be used, combined with incomplete rvalue reference support in that Visual C++ version, evidently results in the wrong overload being selected.

If you want to use C++11/C++14 features, you'd best stay on top of the latest Visual C++ version.

Upvotes: 5

Thomas Hsieh
Thomas Hsieh

Reputation: 731

Your question is probably answered here.

A temporary object cannot bind to a non-const reference. The copy constructor must take a reference to a const object to be able to make copies of temporary objects.

The other thing is that temporary objects shall not be modifiable as they are to be destroyed anytime soon. Holding a reference to temporaries leads to potential modification on nonexistent objects out of carelessness.

Upvotes: 0

Psycho
Psycho

Reputation: 66

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!"

Upvotes: 3

Related Questions