Just reading Stroustrup's C++ Programming Language 4th Ed and in chapter 7 he says:
move(x)
meansstatic_cast<X&&>(x)
where X is the type of x
and
Since move(x) does not move x (it simply produces an rvalue reference to x) it would have been better if move() had been called rval()
My question is, if move()
just turns the variable in to an rval, what is the actual mechanism which achieves the "moving" of the reference to the variable (by updating the pointer)??
I thought move()
is just like a move constructor except the client can use move() to force the compiler??
rvalues are generally temporary values which are discarded and destroyed immediately after creation (with a few exceptions). std::string&&
is a reference to a std::string
that will only bind to an rvalue. Prior to C++11, temporaries would only bind to std::string const&
-- after C++11, they also bind to std::string&&
.
A variable of type std::string&&
behaves much like a bog-standard reference. It is pretty much only in the binding of function signatures and initialization that std::string&&
differs from std::string&
variables. The other way it differs is when you decltype
the reference. All other uses are unchanged.
On the other hand, if a function returns a std::string&&
, it is very different than returning a std::string&
, because the second kind of thing that can be bound to a std::string&&
is the return value of a function returning std::string&&
.
std::move
is the most common way to generate such a function. In a sense, it lies to the context it is in and tells it "I am a temporary, do with me what you will". So std::move
takes a reference to something, and does a cast that makes it pretend to be a temporary -- aka, rvalue.
Move constructors and move assignment and other move-aware functions take an rvalue reference to know when the data they are passed is "scratch" data that they can "damage" to some extent when using it. This is very useful because many types (from containers, to std::function
, to anything that uses the pImpl
pattern, to non-copyable resources) can have their internal state moved much easier than it can be copied. Such a move changes the state of the source object: but because the function is told it is scratch data, that isn't impolite.
So the move
happens not in std::move
, but in the function that understands that the return value of std::move
implies that it is permitted to modify the data in a somewhat destructive manner if that would help it.
The other ways you can get an rvalue, or an indication that the source object is "scratch data", is when you have a true temporary (an anonymous object created as the return of some other function, or one created using function-style constructor syntax), or when you return from a function with a statement of the form return local_variable;
. In both cases, the data binds to rvalue references.
The short version is that std::move
does not move, and std::forward
does not forward, it just indicates that such an action would be allowed at this point, and lets the function/constructor being called decide what to do with that information.
When you are calling move
, you are just telling "Hey, I want to move this object". And when constructor accepts rvalue-reference, it understands it as "Hmm, someone want I move data from this object into myself. So, OK, I'll do it".
std::move
does not moves or changes object, it just "marks" it as "ready-for-moving". And only function, that accepts rvalue reference should implement moving actual object.
This is an example, that describes the text above:
#include <iostream>
#include <utility>
class Foo
{
public:
Foo(std::size_t n): _array(new int[n])
{
}
Foo(Foo&& foo): _array(foo._array)
{
// Hmm, someone tells, that this object is no longer needed
// I will move it into myself
foo._array = nullptr;
}
~Foo()
{
delete[] _array;
}
private:
int* _array;
};
int main()
{
Foo f1(5);
// Hey, constructor, I want you move this object, please
Foo f2(std::move(f1));
return 0;
}
what is the actual mechanism which achieves the "moving" of the reference to the variable (by updating the pointer)??
Passing it to a function (or constructor) that takes an rvalue reference, and moves the value from that reference. Without the cast, variables cannot bind to rvalue references, and so can't be passed to such a function - this prevents variables from being accidentally moved from.
I thought
move()
is just like a move constructor except the client can usemove()
to force the compiler??
No; it's used to convert an lvalue into an rvalue in order to pass it to a move constructor (or other moving function) which requires an rvalue reference.
typedef std::unique_ptr<int> noncopyable; // Example of a noncopyable type
noncopyable x;
noncopyable y(x); // Error: no copy constructor, and can't implicitly move from x
noncopyable z(std::move(x)); // OK: convert to rvalue, then use move constructor
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