In the doc of std::forward
, it gave the following example:
template<class T>
void wrapper(T&& arg)
{
foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get()));
}
Why is forwarding of return value needed here? What's the cases where it is different to the following code:
template<class T>
void wrapper(T&& arg)
{
foo(forward<T>(arg).get());
}
Let's break down the possibilities.
T::get
could return an lvalue reference (which is an lvalue expression), an rvalue reference (which is an xvalue expression), or a prvalue.The
forward
expression will convert the lvalue expression into... an lvalue expression. It will convert the xvalue into... an xvalue. And it will convert a prvalue into an xvalue.C++'s rules about how arguments bind to parameters in overload resolution are the same for prvalue and xvalue expressions. So the last two will always call the same function.
Therefore, the outer
forward
accomplishes nothing. Indeed, it is worse than doing nothing at all. Why?Because prvalues in C++17 and above have guaranteed elision; xvalues do not. If
foo
takes the parameter by value, the additionalforward
will manifest an unnecessary temporary, which will then be moved into the argument. If the type is something more complex than anint
, then there's a decent chance that you're going to lose some performance.So don't forward return values which you are going to pass directly as function arguments. If you need to store the value in an intermediary
auto&&
variable, then you'll need to forward that. But if you're doing it in-situ like this, don't.The edit it was added in claims it is an example for the second overload:
The example is not very good, as is already an rvalue. Actually I don't think the second overload is all that useful, except for making:
work for all expressions (Including if
expression
is an rvalue), but most use cases forstd::forward
are limited to the lvalues ofT&&
andauto&&
.