in scott meyers book he mentioned an implementation for std forward that goes like this (non std conformant)
template <typename T>
T&& forward(typename remove_reference<T>::type& param)
{
return static_cast<T&&>(param);
}
The question is why do we need to remove reference here?
so a typical forward usage would be in a universal reference function as follows:
template <typename T>
void f(T&& fparam)
{
g(forward<T>(fparam)); // assume there is g function.
}
without remove reference, the forward would look like this
template <typename T>
T&& forward(T& param);
now the two cases are:
fparam is rvalue in that case inside f function the T is deduced as non reference object type so the forward call take the param by lvalue reference and cast it to T&& (because T is non reference).
fparam is lvalue then inside f the T is deduced as T& then forward will take (as argument) a reference to an lvalue reference (collapsing to lvalue reference) then the static cast would be T& && which is again lvalue reference.
so why do we need to remove reference from param of forward? does it have to do with disallowing deducing types maybe? can somebody maybe give a detailed explanation.
The question that is referenced as duplicate is not, the answer basically says that the std library uses remove reference but why?
Reference collapsing is the mechanism that leads to universal references (which are really just rvalue references in situations where reference-collapsing takes place) sometimes resolving to lvalue references and sometimes to rvalue references.
std::forward This is a helper function to allow perfect forwarding of arguments taken as rvalue references to deduced types, preserving any potential move semantics involved.
I understand that a forwarding reference is "an rvalue reference to a cv-unqualified template parameter", such as in. template <class T> void foo(T&& ); which means the above function can take both l-value and r-value reference.
If you don't invoke std::forward and only do f(x) , then x will always be a lvalue, so you'll be losing move semantics when needed and may end up with un-necessary copies etc.
That was easy; let’s look at std::forward. std::forward is used as part of perfect forwarding, where we take a bunch of arguments and want to pass them to another function. #include <utility> template <typename Fn, typename ... Args> void call(Fn fn, Args&&... args) { // Forward the arguments to the function. fn(std::forward<Args>(args)...); }
std::forward keeps the reference type of x. So: If x is an rvalue reference then std::forward is = std::move, If x is an lvalue reference then std::forward doesn’t do anything. So for the previous example of ‘wrapper’ function on func (E1 E2, ... En) - } // Awesome!! std::forward is a conditional cast but std::move is an unconditional cast.
†The term forwarding reference derives its name from std::forward () 's name. If a function parameter of a function template is declared as an rvalue reference to cv-unqualified type template parameter of that same function template, we call it a universal reference or a forwarding reference.
Reference collapsing is magic that enables Universal references to work. These rules are basically a logical AND of Lvalue ref implying ‘0’ (zero) with Rvalue ref implying ‘1’ (one) Reference Collapsing Rule#1: Lvalues are infectious. So basically in the above template examples of Univeral reference —
Does it have to do with disallowing deducing types maybe?
Yes, typename std::remove_reference<T>::type
introduces a non-deduced context. It prevents the user from mistakenly writing...
std::forward(something)
...and forces him/her to provide an explicit template argument:
std::forward<T>(something)
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