Reputation: 1677
I've been studying rvalue references (a new concept for me), and am puzzled by a warning I receive in the following class function...
string&& Sampler::Serial() const {
stringstream ss;
.
. [assemble a string value using data members]
.
return ss.str();
}
This compiles successfully, but with the following warning...
..\Metrics\Sampler.cpp:71:16: warning: returning reference to temporary [-Wreturn-local-addr]
return ss.str();
^
I'm fully aware that I'm returning a temporary, as evidenced by the fact that I'm using an rvalue reference as my return type. The code seems to run fine upon execution, so why should this warrant a compiler warning?
The standard answer to similar questions seems to be to copy the return value instead of using a reference, but why should I copy potentially massive amounts of temporary data when I can move it with an rvalue reference? Isn't that why it was invented?
Upvotes: 6
Views: 1547
Reputation:
You're not moving your data. You're creating a local object, creating a reference to that local object, destroying that local object, and then still using that reference.
You should return by value, as you already found. But instead of copying, move the data. That's the safe way of ensuring you don't copy those massive amounts of data.
std::string Sampler::Serial() const {
std::stringstream ss;
.
. [assemble a string value using data members]
.
return std::move(ss.str());
}
Note: the std::move
is technically redundant here, as ss.str()
already returns an rvalue and so would already be moved. I recommend leaving it in anyway. This way works in any situation, so you don't have to think about which form to use: if you want to move, write move
.
As pointed out by T.C., in general, though not in your case, this can prevent RVO. In cases where RVO is possible and where the compiler would implicitly use a move anyway, there is no need to write move
explicitly. For instance:
std::string f() {
std::string x;
...
return x; // not std::move(x)
}
Here, it should already be clear to the reader that x
is a local variable. It's normal for C++ code to return local variables without writing move
, because either the compiler will elide the x
local variable entirely and construct the std::string
object directly in the return slot (whatever that means for your platform), or the compiler will use the move constructor of std::string
implicitly anyway.
Upvotes: 8
Reputation: 217145
You return a (rvalue) reference to object (return by str()
) of temporary object (ss
) which is destroyed at end of scope.
You should return object instead:
string Sampler::Serial() const
Upvotes: 4
Reputation: 65600
This is analogous to returning an lvalue reference to a local variable and puts you on the fast-track to undefined behaviour.
Regardless of whether you return an lvalue reference or an rvalue reference, you are still referencing memory which is going to be destroyed on function exit.
Rvalue reference return types should be reserved for cases when you are referencing an object which has a lifetime longer than the function, but you don't need it anymore, so are fine with it being moved-from for efficiency. For example, you might have a case in which you are temporarily storing some data, but clients can choose to "steal" the data from you. Returning an rvalue reference to the data would be reasonable in that case.
Upvotes: 5