C++: std::move with rvalue reference is not moving contents
explicit Trade(vecstr_t&& vec) : _vec(vec)
{}
In the constructor above, even though vec
is of type rvalue reference to vecstr_t
, it is itself an lvalue. The basic rule to remember is - if it has a name, it's an lvalue.
There are very few contexts where an lvalue may automatically be moved from (such as the return statement of a function that returns an object by value), but a constructor's mem-initializer list is not one of them.
In your example, _vec
is copy constructed from vec
. If you want it to be move constructed instead, use std::move
.
explicit Trade(vecstr_t&& vec) : _vec(std::move(vec))
{}
Now the second call to print
will not print anything. Note that technically the second call could print a non-zero size because the contents of a moved from vector
are unspecified. But on most (probably all) implementations, you'll see an empty vector
.
Live demo
Your comment below says your intent is to accept both rvalues and lvalues, move only in the case of the former, and copy the argument otherwise. As currently written, your constructor will only accept rvalues, and not lvalues. There are a few different options to achieve what you want.
The easiest probably is to change the parameter so that it's taking the argument by value, and then unconditionally move.
explicit Trade(vecstr_t vec) : _vec(std::move(vec))
{}
The drawback with this approach is that you may incur an additional move construction of the vector
, but move constructing a vector
is cheap, and you should go with this option in most cases.
The second option is to create two overloads of the constructor
explicit Trade(vecstr_t&& vec) : _vec(std::move(vec)) {}
explicit Trade(vecstr_t const& vec) : _vec(vec) {}
The drawback with this one is that the number of overloads will increase exponentially as the number of constructor arguments increases.
The third option is to use perfect forwarding.
template<typename V>
explicit Trade(V&& vec) : _vec(std::forward<V>(vec)) {}
The code above will preserve the value category of the argument passed to the constructor when it forwards it to construct _vec
. This means that if vec
is an rvalue, the vecstr_t
move constructor will be called. And if it is an lvalue, it will be copied from.
The drawback with this solution is that your constructor will accept any type of argument, not just a vecstr_t
, and then the move/copy construction in the mem-initializer list will fail if the argument is not convertible to vecstr_t
. This may result in error messages that are confusing to the user.
Why does rvalue object does not get moved to function with rvalue parameter?
std::move
by itself does not actually move anything. That is to say, std::move
alone does not invoke any move constructors or move-assignment operators. What it actually does is effectively cast its argument into an rvalue as if by static_cast<typename std::remove_reference<T>::type&&>(t)
, according to cppreference.
In order for any moving to actually happen, the moved object must be assigned to or used in the move-initialization of something else. For example, it could be used to initialize a member.
void something::consume_ptr(std::shared_ptr<int> && ptr) {
this->ptr = std::move(ptr);
std::cout << "Consumed " << (void*) ptr.get() << std::endl;
}
However, one way to make your pointer get moved without being assigned to anything is to simply pass it by value, causing your pointer to be moved into the parameter.
void consume_ptr(std::shared_ptr<int> ptr) {
std::cout << "Consumed " << (void*) ptr.get() << std::endl;
}
This way can actually be more useful than the rvalue way if you're going to end up assigning the parameter to something, because it allows you to pass stuff in by copy, too, and not just by move.
void consume_ptr_by_rvalue(std::shared_ptr<int> && ptr);
void consume_ptr_by_value(std::shared_ptr<int> ptr);
void do_stuff() {
std::shared_ptr<int> x = /*...*/;
std::shared_ptr<int> y = /*...*/;
// consume_ptr_by_rvalue(x); // Doesn't work
consume_ptr_by_rvalue(std::move(y)); // Risk of use-after-move
std::shared_ptr<int> z = /*...*/;
std::shared_ptr<int> w = /*...*/;
consume_ptr_by_value(z);
consume_ptr_by_value(std::move(w)); // Still risk, but you get the idea
consume_ptr_by_value(make_shared_ptr_to_something()); // Can directly pass result of something
}
Why does std::move copy contents for a rvalue or const lvalue function argument?
Your vector is actually copied, not moved. The reason for this is, although declared as an rvalue reference, vec_
denotes an lvalue expression inside the function body. Thus the copy constructor of std::vector
is invoked, and not the move constructor. The reason for this is, that vec_
is now a named value, and rvalues cannot have names, so it collapses to an lvalue. The following code will fail to compile because of this reason:
void foo(int&& i)
{
int&& x = i;
}
In order to fix this issue, you have to make vec_
nameless again, by calling std::move(vec_)
.
std::move Not Working on RValue Reference Function
std::move
only casts to Rvalue reference.
foo
takes Rvalue ref to vector<int>
. By move(vecNumbers)
you get vector<int>&&
. Inside foo
you just access vecNumbers
which is defined in main
. You didn't do any action which changed the content of this vector.
If you really want to move (steal) content of vecNumbers
you have to call either move constructor or move assignment operator. Inside foo
you could do this in this way:
void foo( std::vector<int>&& value)
{
std::vector<int> v1{std::move(value)}; // invoke move ctor which steals content of value
std::cout<<"size in Function:"<<value.size()<<"\n";
}
or you can change signature of foo to be:
void foo(std::vector<int> value) {
}
then when you call
foo(std::move(vecNumbers))
move constructor of vector<T>
is called which moves vecNumbers
to value
inside foo
.
What happens when std::move is called on a rvalue reference?
string constructed = s;
This does not cause a move because s
is not an rvalue. It is an rvalue reference, but not an rvalue. If it has a name, it is not an rvalue. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression.
string constructed = std::move(s);
This causes a move because std::move(s)
is an rvalue: it's a temporary and its type is not lvalue reference.
There are no other moves in the program (std::move
is not a move, it's a cast).
why using std::move and assign to rvalue does not steal internal content?
It is because c
is declared as a reference (rvalue reference) to a. It is not a different string. In order to "still" (i.e. call the move constructor), c
needs to be declared a string
. Then, string a is "moved" to string c:
int main()
{
string a="4";
string c = move(a);
cout<< "a :" <<a << ":" <<endl;
cout << "c :" << c << ":"<< endl;
}
Output is:
a ::
c :4:
This code moves an rvalue to an lvalue though, which is the way it's supposed to work. However, it sounds like you're trying to move an lvalue to an rvalue, but that is not what you're doing. c
is an rvalue, but move(a)
is also an rvalue. std::move()
casts a
to an rvalue reference. Think of a reference as something similar to a pointer, it is not a string
. You can't move or copy a string to it. It just refers to the string. Anyway, I don't think that you can move lvalues to rvalues. I can't think of any case.
Why do I have to call move on an rvalue reference?
As skypjack correctly comments, accessing an object through its name always results in an lvalue reference.
This is a safety feature and if you think it through you will realise that you are glad of it.
As you know, std::move
simply casts an l-value reference to an r-value reference. If we use the returned r-value reference immediately (i.e. un-named) then it remains an r-value reference.
This means that the use of the r-value can only be at the point in the code where move(x)
is mentioned. From a code-reader's perspective, it's now easy to see where x's state became undefined.
so:
1: auto x = make_x();
2: auto&& r = std::move(x);
3: // lots of other stuff
35: // ...
54: // ...
55: take_my_x(r);
does not work. If it did, someone maintaining the code would have a hard time seeing (and remembering) that x (defined on line 1) enters an undefined state on line 55 through a reference taken on line 2.
This is a good deal more explicit:
1: auto x = make_x();
2: //
3: // lots of other stuff
35: // ...
54: // ...
55: take_my_x(std::move(x));
Rvalue reference: Why aren't rvalues implicitly moved?
Consider this scenario:
void foo(std::string x) {}
void bar(std::string y) {}
void test(std::string&& str)
{
// to be determined
}
We want to call foo
with str
, then bar
with str
, both with the same value. The best way to do this is:
foo(str); // copy str to x
bar(std::move(str)); // move str to y; we move it because we're done with it
It would be a mistake to do this:
foo(std::move(str)); // move str to x
bar(std::move(str)); // move str to y...er, except now it's empty
Because after the first move the value of str
is unspecified.
So in the design of rvalue references, this implicit move is not there. If it were, our best way above would not work because the first mention of str
would be std::move(str)
instead.
Why move return an rvalue reference parameter need to wrap it with std::move()?
In passThroughMove(Widget&& w), w's type is already rvalue reference, std::move(w) just cast it into rvalue reference again.
So std::move(w) returns an rvalue reference, just as w itself.
No, std::move(w)
casts to rvalue, while rvalue references are lvalues.
Both functions passThroughMove
and passThrough
return by value.
They differ, however, in the way they create internally such return value.
Internally, passThroughMove
creates its return value by move. A new Widget
object (the return value) is created by moving into it, that's the effect of std::move
on the return value. passThrough
on the other hand creates its own return value by copy.
The fact that the assignment
Widget wt2 = passThrough(std::move(w2));
is done from an rvalue does not change the fact that passThrough
is forced to create its return value by copy.
In the output of the code you see the effect of the above semantics plus RVO. Without RVO both assignments should result into two additional move-constructions, which are optimized away.
Related Topics
Should Std::Unique_Ptr<Void> Be Permitted
C++ 128/256-Bit Fixed Size Integer Types
Insert into an Stl Queue Using Std::Copy
C++: Calling Member Function via Pointer
How to Config Cmake for Strip File
What Is the Maximum Memory Available to a C++ Application on 32-Bit Windows
Visual Studio 2015 Code Analysis C6386 Warns of Buffer Overrun
Disable Sleep Mode in Windows Mobile 6
Why Can't I Use <Experimental/Filesystem> with G++ 4.9.2
How to Overload Array Index Operator for Wrapper Class of 2D Array
System':A Namespace with This Name Does Not Exist
G++ -Wall Not Warning About Double-> Int Cast
Fatal Error C1083: Cannot Open Include File: 'Xyz.H': No Such File or Directory
Should "Delete This" Be Called from Within a Member Method
C++: Function Pointer to Functions with Variable Number of Arguments
What Does C4250 Vc++ Warning Mean
What Are the Reasons That Extending the Std Namespace Is Considered Undefined Behavior