Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 606
Junekey Jeon Avatar asked Apr 05 '15 15:04

Junekey Jeon


2 Answers

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.

like image 50
Ben Voigt Avatar answered Oct 15 '22 06:10

Ben Voigt


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!"
like image 45
Psycho Avatar answered Oct 15 '22 07:10

Psycho