Given the following reference collapsing rules
T& &-->T&T&& &-->T&T& &&-->T&T&& &&-->T&&
The third and fourth rule imply that T(ref qualifer) && is the identity transformation, i.e. T& stays at T& and T&& stays at T&&. Why do we have two overloads for std::forward? Couldn't the following definition serve all purposes?
template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>>
T&& forward(const typename std::remove_reference<T>::type& val) {
return static_cast<T&&>(const_cast<T&&>(val));
}
Here the only purpose the const std::remove_reference<T>& serves is to not make copies. And the enable_if helps ensure that the function is only called on non const values. I'm not entirely sure whether the const_cast is needed since it's not the reference itself that's const.
Since forward is always called with explicit template parameters there are two cases we need to consider:
forward<Type&>(val)Here the type ofTinforwardwill beT&and therefore the return type will be the identity transformation toT&forward<Type&&>(val)Here the type ofTinforwardwill beT&&and therefore the return type will be the identity transformation toT&&
So then why do we need two overloads as described in http://en.cppreference.com/w/cpp/utility/forward?
Note: I am not sure if std::forward is ever used with const types, but I disabled forward in that case, because I have never seen it used like that. Also move semantics don't really make sense in that case either.
A good place to start would be Howard Hinnant's answer and paper on std::forward().
Your implementation handles all the normal use-cases correctly (T& --> T&, T const& --> T const&, and T&& --> T&&). What it fails to handle are common and easy-to-make errors, errors which would be very difficult to debug in your implementation but fail to compile with std::forward().
Given these definitions:
struct Object { };
template <typename T, typename = std::enable_if_t<!std::is_const<T>::value>>
T&& my_forward(const typename std::remove_reference<T>::type& val) {
return static_cast<T&&>(const_cast<T&&>(val));
}
template <class T>
void foo(T&& ) { }
I can pass non-const references to const objects, both of the lvalue variety:
const Object o{};
foo(my_forward<Object&>(o)); // ok?? calls foo<Object&>
foo(std::forward<Object&>(o)); // error
and the rvalue variety:
const Object o{};
foo(my_forward<Object>(o)); // ok?? calls foo<Object>
foo(std::forward<Object>(o)); // error
I can pass lvalue references to rvalues:
foo(my_forward<Object&>(Object{})); // ok?? calls foo<Object&>
foo(std::forward<Object&>(Object{})); // error
The first two cases lead to potentially modifying objects that were intended to be const (which could be UB if they were constructed const), the last case is passing a dangling lvalue reference.
来源:https://stackoverflow.com/questions/38344332/why-does-stdforward-have-two-overloads