I am curious why the below implementation is wrong?
template<typename T> // For lvalues (T is T&),
T&& std::forward(T&& param) // take/return lvalue refs.
{ // For rvalues (T is T),
return static_cast<T&&>(param); // take/return rvalue refs.
}
According to "Effective Modern C++" book, this is the possible implementation of std::forward
which is understandable.
template<typename T>
T&& forward(remove_reference_t<T>& param)
{
return static_cast<T&&>(param);
}
A user in the comments here answered like this:
In the first case, you could get away with std::forward(x), but in the second you must explicitly provide the template parameters, as they cannot be inferred.
Questions
std::forward(x)
is possible, but for
the 2nd case it is not? I.e., why in the 2nd case type deduction
won't work?std::forward(x)
? I understand that it doesn't match with
the standard's std::forward<T>(x)
signature, but then another questions comes: why we need to specify type explicitly here?(1) is an identity function, it returns the argument as is, which makes it useless.
(2) only accepts lvalues, and returns either an lvalue or an rvalue depending on T
. Note that std::forward
also has a second overload that accepts an rvalue and returns an rvalue, but it's mostly useless.
I.e., why in the 2nd case type deduction won't work?
Because deduction can't propagate through typedefs (remove_reference_t
). And indeed, how could you unremove_reference
, when the original could've been T
, T &
, or T &&
.
What is wrong with writing
std::forward(x)
?
The whole point of forwarding is to take an lvalue, and to conditionally either cast it to an rvalue or keep as an lvalue. This requires external information to decide whether to cast, and in this case it comes from T
.
In theory it's possible to get the syntax you want (without the template argument), but this requires a macro, e.g. just #define FWD(...) decltype(__VA_ARGS__)(__VA_ARGS__)
.
In this case the extra information comes from decltype(x)
, which has the magical ability to detect the rvalue-reference-ness of the variable, even though the variable name by itself is always an lvalue.