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?
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.
Wow, when I compile this with...
Bar(Bar &that)
to Bar(const Bar &that)
then "Default! Move!"Bar(Bar &that)
with Bar(Bar &&that)
(so that the move ctor is defined first) then you'll actually see "Default! Move!"If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With