If I'm not wrong, I think that both a const reference and a rvalue reference can bind to a rvalue. Is there any practical difference between a function that returns the former and a function that returns the latter?
EDIT. I cannot modify the former, but why would I be interested in modifying a rvalue? Does it make sense?
If the thing you are returning by reference is logically part of your this object, independent of whether it is physically embedded within your this object, then a const method needs to return by const reference or by value, but not by non-const reference.
An lvalue is an expression that yields an object reference, such as a variable name, an array subscript reference, a dereferenced pointer, or a function call that returns a reference. An lvalue always has a defined region of storage, so you can take its address. An rvalue is an expression that is not an lvalue.
Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.
Typically rvalues are temporary objects that exist on the stack as the result of a function call or other operation. Returning a value from a function will turn that value into an rvalue. Once you call return on an object, the name of the object does not exist anymore (it goes out of scope), so it becomes an rvalue.
A const
lvalue reference can bind to anything. An rvalue reference can only bind to non-const
rvalues.
non-const lvalue const lvalue non-const rvalue const rvalue
const T& yes yes yes yes
T&& no no yes no
As you can see, they are very different.
In addition, if a function call returns an lvalue reference, that expression is an lvalue, but if a function call returns an rvalue reference to object, that expression is an xvalue.
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
As for when you would want to modify an rvalue - well this is precisely what move semantics are all about. Consider the following function call:
void func(std::string);
func(std::string("Hello"));
The expression std::string("Hello")
is an rvalue that creates a temporary object. When initializing the std::string
parameter with this rvalue, it will choose the constructor that takes an rvalue reference - the move constructor. This constructor then steals things from the rvalue, which is typically much faster than doing a full copy. We can steal from it because we know it's temporary.
As for when you should return const
lvalue references or rvalue references:
Returning a const
lvalue reference is most commonly used when you want to give access to read an "internal" object (perhaps a member of a class), but not allow people to modify it.
Returning an rvalue reference is most commonly used (not common at all) when you want to allow calling code to move from an "internal" object (perhaps a member of a class). So instead of moving from a temporary returned object (as they would when returning by value), they literally move from the internal object.
This could also be achieved with a non-const
lvalue reference, but then they would have to explicitly std::move
it.
So it's not very likely that you'll need to return an rvalue reference.
Not that std::forward
has a return type that looks like T&&
. However, this is deceptive, because it may or may not be an rvalue reference depending on the type of T
. See universal references.
Is there any practical difference between a function that returns the former and a function that returns the latter?
The question seems to be ill-formed. A function that returns a constant lvalue-reference provides access to an object only for reading, while a function that returns an rvalue-reference provides access for moving which means that the caller can take the contents of the referred object and move it to a different object. They are not comparable by any means.
In both cases, the references must point to an object whose lifetime spans beyond the end of the function that is returning it, as otherwise the caller will trip with undefined behavior on using that reference.
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