Reputation: 1472
I'm playing with move constructors and move assignments and i've stumbled on this problem. First code:
#include <iostream>
#include <utility>
class Foo {
public:
Foo() {}
Foo(Foo&& other) {
value = std::move(other.value);
other.value = 1; //since it's int!
}
int value;
private:
Foo(const Foo& other);
};
void Bar(Foo&& x) {
std::cout << "# " << x.value << std::endl;
}
int main() {
Foo foo;
foo.value = 5;
Bar(std::move(foo));
std::cout << foo.value << std::endl;
return 0;
}
To my mind, when i use:
Bar(std::move(foo));
Program should "move" foo object to temp object created using move constructor in Bar function. Doing so would leave foo object's value equal zero. Unfortunatly it seams that object held in Bar function as parameter is some sort of reference, since it doesnt "move" original value but using Bar's parameter i can change it.
Would someone mind expalining me why i see in console:
#5
5
instead of
#5
0 //this should be done by move constructor?
Upvotes: 11
Views: 34325
Reputation: 2036
Bar function should not take reference &&
(this syntax is valid only in move constructor declaration/definition):
void Bar(Foo x) { ... } // not "Foo&& x"
To call move constructor of temporary argument object in function Bar:
Bar( std::move( x ) );
To call copy constructor:
Bar( x );
Upvotes: 2
Reputation: 15468
Compare these two functions:
void Bar(Foo&& x) {
std::cout << "# " << x.value << std::endl;
}
vs.
void Bar(Foo&& x) {
std::cout << "# " << x.value << std::endl;
Foo y=std::move(x);
}
Both take an rvalue reference, but only the second calls the move constructor. Consequently, the output of the first is
# 5
5
whereas the output of the second -- since the value of foo
is changed -- is:
# 5
1
EDIT: This is a question I had as well some time ago. My mistake then was to assume that the creation of an rvalue reference directly invokes the call of a move constructor. But, as was mentioned here before, std::move
doesn't do anything at runtime, it just changes the type to an rvalue-reference. The move constructor is only invoked when you "move" your rvalue reference into another object as above.
Upvotes: 2
Reputation: 56547
When you write value = std::move(other.value);
you should understand that std::move
does not move anything. It just converts its parameter to a rvalue reference, then, if the left hand side has a move constructor/assignment operator, the left hand side deals with it (and how to actually move the object). For plain old types (PODs), std::move
doesn't really do anything, so the old value remains the same. You are not "physically" moving anything.
Upvotes: 9
Reputation: 392921
An rvalue reference is (surprise:) a reference indeed.
You can move from it, but std::move
does not move.
So, if you don't move from it, you'll actually operate on the rvalue object (through the rvalue reference).
The usual pattern would be
void foo(X&& x)
{
X mine(std::move(x));
// x will not be affected anymore
}
However, when you do
void foo(X&& x)
{
x.stuff();
x.setBooDitty(42);
}
effectively, X&& is just acting as a traditional reference
Upvotes: 25