I would like to implement a function that fills up a vector and then returns an rvalue reference. I tired something like:
std::vector<int> &&fill_list() {
std::vector<int> res;
... do something to fill res ...
return res;
}
int main(int argc, char **argv) {
std::vector<int> myvec = fill_list();
return 0;
}
but that doesn't work, I get the following error:
error: invalid initialization of reference of type 'std::vector<int>&&' from expression of type 'std::vector<int>'
So, all in all, how is the right way of doing it? I don't think I get rvalue references just yet.
You seem to be confused as to what an rvalue reference is and how it relates to move semantics.
First thing's first: && does not mean move. It is nothing more than a special reference type. It is still a reference. It is not a value; it is not a moved value; it is a reference to a value. Which means it has all of the limitations of a reference type. Notably, it must refer to a value that still exists. So returning a dangling r-value reference is no better than returning a dangling l-value reference.
"Moving" is the process of having one object claim ownership of the contents of another object. R-value references facilitate move semantics, but simply having a && does not mean anything has moved. Movement only happens when a move constructor (or move assignment operator) is called; unless one of those two things is called, no movement has occurred.
If you wish to move the contents of a std::vector out of your function to the user, you simply do this:
std::vector<int> fill_list() {
std::vector<int> res;
... do something to fill res ...
return res;
}
Given this usage of fill_list():
std::vector<int> myvec = fill_list();
One of two things will happen. Either the return will be elided, which means that no copying or moving happens. res is constructed directly into myvec. Or res will be moved into the return value, which will then perform move-initialization of myvec. So again, no copying.
If you had this:
std::vector<int> myvec;
myvec = fill_list();
Then again, it would be moved into. No copying.
C++11 knows when it's safe to implicitly move things. Returning a value by value rather than by reference or something is always a safe time to move. Therefore, it will move.
The return statement is an error because you atempt to bind an rvalue reference (the return type) to an lvalue (the vector res). An rvalue reference can only be bound to an rvalue.
Also, as others already mentioned, returning a local variable when the return type is a reference type is dangerous, because the local object will be destroyed after the return statement, then you get a reference that refers to an invalid object.
If you want to avoid the copy construction during the return statement, just using a non-reference type might already works due to a feature called copy elision. the vector res in your fill_list function may be directly constructed into the vector myvec in your main function, so no copy or move construction is invoked at all. But this feature is allowed by Standard not required, some copy construction is not omitted in some compiler.
For a discussion of rvalue references you can read what Bjarne Stroustrup, the author of C++, has to say about them here:
http://www2.research.att.com/~bs/C++0xFAQ.html#rval
Addressing your specific example, the short answer is that due to the Named Return Value Optimization - which is a de facto standard C++ compiler feature even pre-C++11 - if you simply return-by-value the compiler will tie up res and myvec efficiently like you want:
std::vector<int> fill_list() {
std::vector<int> res;
... do something to fill res ...
cout << &res << endl; // PRINT POINTER
return res;
}
int main(int argc, char **argv) {
std::vector<int> myvec = fill_list();
cout << &myvec << endl; // PRINT POINTER
return 0;
}
The two "PRINT POINTER" lines will print the same pointer in both cases.
The vector myvec and the vector res in the above will be the same vector with the same storage. No copy constructor or move constructor will be called. In a sense res and myvec will be two aliases of the same object.
This is even better than using a move constructor. myvec is constructed and "filled up" in-place.
The compiler achieves this by compiling the function in an "inplace" mode overlaying an immediate stack position in the callers stack frame with the callees local result variable, and simply leaving it there after the callee returns.
In this circumstance we say that the constructor has been elided. For more information see here:
http://en.wikipedia.org/wiki/Return_value_optimization
In the event that you were assigning the result of fill_list in a non-constructor context, than as a return-by-value results in an xvalue (short for "expiring" value, a type of rvalue), the move assignment operator of the target variable would be given preference if it is available.
If you just remove the && from your function it should work, but it will not be a reference. fill_list() will create a vector and return it. During the return a new vector will be created. The first vector that was created inside fill_list() will be copied to the new vector and then will be destroyed. This is the copy constructor's work.
Related
I try to understand the concept of move semantic and did some tests. I have the following function:
// testing the move semantic when passing
// argument to be modified without copying
void process_copy( std::vector<int>&& aVec)
{
std::cout << "Move semantic\n";
aVec.push_back(42);
}
now in the main function, the following:
int main()
{
std::vector<int> w_vec = {1,2,3,4,5};
process_copy( std::move(w_vec));
}
I expect that the w_vec is now empty since I pass it with move cast (cast lvalue to rvalue). But the result is w_vec contains 6 elements now (42 has been added to the vector).
Something that I miss? Is std::vector is a moveable object?
An image of my CoreCpp 2019 t-shirt might be relevant here.
This is the front.
It's interesting to note that this behavior of std::move was a bit surprising even for Howard Hinnant himself, the man behind rvalue and move semantics. However he got a very thorough answer there.
What you may be missing is that you are just binding an rvalue reference to the vector you pass; you are moving no vector object at all.
The process_copy() parameter is of type std::vector<int>&&, i.e., an rvalue reference to std::vetor<int>:
void process_copy(std::vector<int>&& aVec)
By using std::move() when calling process_copy() as in:
process_copy(std::move(w_vec));
You are just making it possible to bind this reference – i.e., process_copy()'s parameter, aVec – to the argument – i.e., the vector w_vec. That is, the following does not compile:
process_copy(w_vec);
Because you can't bind an rvalue reference (aVec) to an lvalue (w_vec).
If you want the vector argument w_vec to be moved when calling process_copy(), then you could have the function to take an std::vector<int> by value instead and move construct this parameter at the moment of calling the function:
void process_copy(std::vector<int> aVec) // <-- not a reference
{
aVec.push_back(42);
}
By marking the vector argument with std::move() when calling this process_copy(), the parameter aVec will be move constructed – so the vector argument will end up in a moved-from state.
This program tries to move a string out of a function and use it for the construction of another string:
#include <iostream>
#include <string>
#include <utility>
std::string && Get_String(void);
int main(){
std::string str{Get_String()};
std::cout << str << std::endl;
return 0;
}
std::string && Get_String(void){
std::string str{"hello world"};
return std::move(str);
}
The program compiles, but segfaults upon execution.
This was my rationale: Get_String will create a local string. A copy of that string will need to be made and returned before the string goes out of scope. That copy will be used to construct the string in main. However, If I moved the the string out of the function, no copy would need to be made.
In an attempt to understand move semantics, could someone explain why what I'm doing, probably makes no sense. Is it possible to move objects out of a function?
EDIT:
It compiles and runs correctly if I change the function signature from:
std::string && Get_String(void);
to
std::string Get_String(void);
Is it still more efficient to move the string during the return in this case?
Given this example,
X foo ()
{
X x;
return x;
}
the following behavior is guaranteed:
• If X has an accessible copy or move constructor, the compiler may
choose to elide the copy. This is the so-called (named) return value
optimization ((N)RVO), which was specified even before C++11 and is
supported by most compilers.
• Otherwise, if X has a move constructor, x is moved.
• Otherwise, if X has a copy constructor, x is copied.
• Otherwise, a compile-time error is emitted.
Note also that returning an rvalue reference is an error if the returned object is a local nonstatic object:
X&& foo ()
{
X x;
return x; // ERROR: returns reference to nonexistent object
}
An rvalue reference is a reference, and returning it while referring to a local object means that you
return a reference to an object that doesn’t exist any more. Whether std::move() is used doesn’t
matter.
std::move() doesn't really move object; it only turns lvalues into rvalues.
That Get_String function binds an rvalue reference to a function-local object. Rvalue references are useful for things that are about to be destroyed, but just as bad as lvalue references for things that have already been destroyed.
To move a local object out of a function, you just return by class type:
std::string Get_String(void) {
std::string str{"hello world"};
return str;
}
If the compiler doesn't manage to eliminate the copy/move entirely, then the return value that the caller gets will be constructed using a move constructor, not a copy constructor, as long as the return expression is:
a temporary, or
a single identifier naming something with automatic storage duration (like str above), or
std::move(something)
(So you could still have return std::move(str); to be explicit, but it's not necessary here.)
I have a very basic question: is it a good idea to return a std::vector<A> using std::move? For, example:
class A {};
std::vector<A> && func() {
std::vector<A> v;
/* fill v */
return std::move(v);
}
Should I return std::map, std::list.. etc... in this way?
You declare a function to return by r-value reference - this should almost never be done (if you return the local object by reference, you will end up with a dangling reference). Instead declare the function to return by value. This way the caller's value will be move constructed by the r-value returned by the function. The returned value will also bind to any reference.
Secondly, no, you should not return using an explicit std::move as this will prevent the compiler to use RVO. There's no need as the compiler will automatically convert any l-value reference returned to an r-value reference if possible.
std::vector<A> func() {
std::vector<A> v;
/* fill v */
return v; // 'v' is converted to r-value and return value is move constructed.
}
More info:
Using std::move() when returning a value from a function to avoid to copy
Is there any case where a return of a RValue Reference (&&) is useful?
No, it's not. This will in fact prevent copy elision in some cases. There is even a warning in some compilers about it, called -Wpessimizing-move.
In agreement with other answers, just return it by value, changing the return type to simply be std::vector<A>, and the compiler will take care of calling the move constructor when needed.
You could take a look at this post I just found, which seems to explain it in much detail (although I haven't read it through myself):
https://vmpstr.blogspot.hu/2015/12/redundant-stdmove.html
Both gcc and clang compiler with enabled optimization generate same binary code for both case when you return local variable and when you write std::move().
Just return a value.
But using && and noexcept specifier is useful if you create moving constructor and moving operator= for your custom class
Is the following case of std::move superfluous?
std::string member;
obj(std::initializer_list<std::string> p_list)
: member {std::move(join(p_list))}
{}
This is the join function:
std::string join(string_initializer_list p_list) {
size_t size {};
for (auto const & s : p_list) {
size += s.size();
}
std::string output;
output.reserve(size);
for (auto const & s : p_list) {
output.append(s);
}
return output;
}
No, you don't need std::move. The function of std::move is to cast any value to an rvalue. Your function already returns an rvalue, thus the cast has no effect as far as binding the result to a reference is concerned (which is what you're after in order to initialize member from an rvalue).
In fact, using std::move actively inhibits copy elision, so it is a strict pessimization:
std::string s = join({}); // construct from prvalue, elidable,
// elision mandatory in C++17
std::string s = std::move(join({})); // temporary object must be constructed,
// s is initialized by moving from the
// temporary
In the first form, std::string s = join({});, copy elision means that the returned object of join is constructed directly in place of s (no temporary objects is constructed and copy or move is made), and moreover, the output variable in the function body is also elided and constructed directly in the return value, i.e. in s. With std::move, the first elision step is not available.
Given your join function is sane, and returns std::string, yes, the std::move would be superfluous; the return from join is an r-value already.
Beyond that, w/o std::move, copy elision means that it could construct the result in place without std::move, while using std::move could force it make a temporary string, invoke the move constructor to initialize member, then destroy the temporary; not a lot of work (a few pointer copies largely), but more than you need to do.
For clarity: any function that returns by value and hence returns an rvalue does not need std::move to be applied, and as others have already pointed out it is detrimental to performance optimisations.
e.g.
SomeClass fn()
{
...
}
// Move not needed, detrimental, as the function result is already an rvalue
SomeClass x = std::move(fn());
just do this as usual before c++11:
SomeClass x = fn();
Generally speaking use std::move(x) when x has a name. i.e. it is an identifier, it is not useful on temporaries except for special circumstances (in which you want to prevent copy elision, for example)
On a side note: Also don't try to use std::move to force the "strong exception gurantee" (for example when you want a struct wholly copied or left alone [i.e. consisent] in the face of potential exceptions.) as std::move can throw depending on the moved type and this type's exception policy can change under you. Instead use a wrapper function with a static assert combined with noexcept to enfore this at compile time. See Is there facility for a strong guaranteed exchange in C++
A C++Next blog post said that
A compute(…)
{
A v;
…
return v;
}
If A has an accessible copy or move constructor, the compiler may choose to elide the copy. Otherwise, if A has a move constructor, v is moved. Otherwise, if A has a copy constructor, v is copied.
Otherwise, a compile time error is emitted.
I thought I should always return the value without std::move
because the compiler would be able to figure out the best choice for users. But in another example from the blog post
Matrix operator+(Matrix&& temp, Matrix&& y)
{ temp += y; return std::move(temp); }
Here the std::move is necessary because y must be treated as an lvalue inside the function.
Ah, my head almost blow up after studying this blog post. I tried my best to understand the reasoning but the more I studied, the more confused I became. Why should we return the value with the help of std::move?
So, lets say you have:
A compute()
{
A v;
…
return v;
}
And you're doing:
A a = compute();
There are two transfers (copy or move) that are involved in this expression. First the object denoted by v in the function must be transferred to the result of the function, i.e. the value donated by the compute() expression. Let's call that Transfer 1. Then, this temporary object is transferred to create the object denoted by a - Transfer 2.
In many cases, both Transfer 1 and 2 can be elided by the compiler - the object v is constructed directly in the location of a and no transferring is necessary. The compiler has to make use of Named Return Value Optimization for Transfer 1 in this example, because the object being returned is named. If we disable copy/move elision, however, each transfer involves a call to either A's copy constructor or its move constructor. In most modern compilers, the compiler will see that v is about to be destroyed and it will first move it into the return value. Then this temporary return value will be moved into a. If A does not have a move constructor, it will be copied for both transfers instead.
Now lets look at:
A compute(A&& v)
{
return v;
}
The value we're returning comes from the reference being passed into the function. The compiler doesn't just assume that v is a temporary and that it's okay to move from it1. In this case, Transfer 1 will be a copy. Then Transfer 2 will be a move - that's okay because the returned value is still a temporary (we didn't return a reference). But since we know that we've taken an object that we can move from, because our parameter is an rvalue reference, we can explicitly tell the compiler to treat v as a temporary with std::move:
A compute(A&& v)
{
return std::move(v);
}
Now both Transfer 1 and Transfer 2 will be moves.
1 The reason why the compiler doesn't automatically treat v, defined as A&&, as an rvalue is one of safety. It's not just too stupid to figure it out. Once an object has a name, it can be referred to multiple times throughout your code. Consider:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(a);
}
If a was automatically treated as an rvalue, doSomething would be free to rip its guts out, meaning that the a being passed to doSomethingElse may be invalid. Even if doSomething took its argument by value, the object would be moved from and therefore invalid in the next line. To avoid this problem, named rvalue references are lvalues. That means when doSomething is called, a will at worst be copied from, if not just taken by lvalue reference - it will still be valid in the next line.
It is up to the author of compute to say, "okay, now I allow this value to be moved from, because I know for certain that it's a temporary object". You do this by saying std::move(a). For example, you could give doSomething a copy and then allow doSomethingElse to move from it:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(std::move(a));
}
An implicit move of function results is only possible for automatic objects. An rvalue reference parameter does not denote an automatic object, hence you have to request the move explicitly in that case.
The first takes advantage of NVRO which is even better than moving. No copy is better than a cheap one.
The second cannot take advantage of NVRO. Assuming no elision, return temp; would call the copy constructor and return std::move(temp); would call the move constructor. Now, I believe either of these have equal potential to be elided and so you should go with the cheaper one if not elided, which is using std::move.
In C++17 and later
C++17 changed the definition of value categories so that the kind of copy elision you describe is guaranteed (see: How does guaranteed copy elision work?). Thus if you write your code in C++17 or later you should absolutely not return by std::move()'ing in this case.