As we have known, in most common cases, T&&
means "this is a temporary object". However, if one wants to return a temporary object from a function, he/she can declare the function as follows:
template<class T>
T f()
{
T t;
......
return t;
}
or (Note: Not Correct)
template<class T>
T&& f()
{
T t;
......
return t;
}
But I think the latter is overdid, because the former is enough and backward compatible.
Yet, I also find the std::forward()
's return type is declared as T&&, so I'm sure my understanding about this is incomplete.
My real question is: When and where should we declare the return type of a function as T&&?
In your example, the T&&
is wrong, it's a dangling reference.
But std::forward
doesn't return an rvalue reference to a local variable in its own definition, it returns an rvalue reference to its by-rvalue-reference argument (or an lvalue reference to a by-lvalue-reference argument).
You should return an rvalue reference only if you want the caller of your function to be able to move from whatever that reference refers to.
Normally that will only be if the purpose of the function is to provide move access to some significant object (perhaps which already exists). So that includes std::move
(which allows you to move from an lvalue), and similarly you might write an accessor function specifically designed for users to move from a data member of some object, or an element of some container. If the object itself isn't significant, only the value, then you can return by value.
As grizzly says, sometimes due to reference collapsing you can take advantage of tricks which mean that you type T&&
in your code, but when T
is already an lvalue-reference type T&&
is the same lvalue reference type. std::forward
uses that trick. That is to say, because of reference collapsing T&&
doesn't mean "rvalue reference to T", it means "T if T is a reference type, otherwise rvalue reference to T".
T&&
doesn't necessarily mean that the result is an rvalue. When used with a template parameter &&
denotes a universal reference, which can either be an rvalue or an lvalue reference. More specifically: If T
is an lvalue reference type foo&
, T&&
is actually an lvalue refere to foo
, otherwise it denotes an rvalue reference.
This can be used to write functions, which take any kind of argument:
template<typename T> void foo(T&&);
bar a;
const bar b;
foo(a);//T and T&& will both be bar&
foo(b);//T and T&& will both be const bar&
foo(bar());//T will be bar, T&& will be bar&&
With this in mind std::forward
is called with a T&
and casts it to T&&
, where T
is explicitly stated. So if the original function parameter was an lvalue reference, it will return one, otherwise it will return an rvalue reference, enabling perfect forwarding.
As for when to use it as a return type: Rarely, though it might be useful to avoid copies when arguments are passed through, for example:
template<typename T> T&& foo(T&& bar) {/*some ops*/ return std::forward<T>(bar);}
The answer depends on whether your function is a template function or not. In your question it was, but let's first take a look at if it isn't:
Non-Template
T&&
is not meaningless as a return type. The reason is that it, in fact, does not mean "temporary object". It means "rvalue reference". The difference is subtle, but can be highlighted with a class of functions for which this return type is relevant. Namely, what if we want to return a reference, to an rvalue, but the object it refers to is not a local object of our function?
T&& return_rvalue(/*some data*/)
{
T&& t = Get_a_reference();
// Do something fascinating.
return static_cast<T&&>(t);
}
One very special case of this pattern is the function std::move
, which takes any reference and returns it a corresponding rvalue reference. Naturally, in real code, you should of course use std::move
rather than performing the cast directly, since this more clearly shows your intention.
Template
If T
is a template parameter, T&&
is what Scott Meyers refers to as a Universal Reference. This means the type of T&&
will be figured out using reference collapsing... In short, it means T&&
is an rvalue if T
i not a reference type, and an lvalue reference if it is..
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