I'm reading Overview of the New C++ (C++11/14) (PDF only), at Slide 288 it gives an implementation of std::forward
:
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. }
And then gives another implemention in text:
The usual
std::forward
implementation is:
template<typename T> struct identity { typedef T type; }; template<typename T> T&& forward(typename identity<T>::type&& param) { return static_cast<identity<T>::type&&>(param); }
What is the difference? Why is latter the usual implementation?
The idiomatic use of std::forward is inside a templated function with an argument declared as a forwarding reference , where the argument is now lvalue , used to retrieve the original value category, that it was called with, and pass it on further down the call chain (perfect forwarding).
What is Perfect Forwarding. Perfect forwarding allows a template function that accepts a set of arguments to forward these arguments to another function whilst retaining the lvalue or rvalue nature of the original function arguments.
Because for std::forward to work as intended(, i.e. to faitfully pass the original type info), it is meant to be used INSIDE TEMPLATE CONTEXT, and it must use the deduced type param from the enclosing template context, instead of deducing the type param by itself(, since only the enclosing templates have the chance to ...
When t is a forwarding reference (a function argument that is declared as an rvalue reference to a cv-unqualified function template parameter), this overload forwards the argument to another function with the value category it had when passed to the calling function.
The problem with the first is that you can write std::forward(x)
, which doesn't do what you want, since it always produces lvalue references.
The argument in the second case is a non-deduced context, preventing automatic deduction of the template argument. This forces you to write std::forward<T>(x)
, which is the right thing to do.
Also, the argument type for the second overload should be typename identity<T>::type&
because the input to idiomatic use of std::forward
is always an lvalue.
Edit: The standard actually mandates a signature equivalent to this one (which, incidentally, is exactly what libc++ has):
template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept; template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept;
The implementation in libc++ uses std::remove_reference
and two overloads. Here is the source (after removing some macros):
template <class T> inline T&& forward(typename std::remove_reference<T>::type& t) noexcept { return static_cast<T&&>(t); } template <class T> inline T&& forward(typename std::remove_reference<T>::type&& t) noexcept { static_assert(!std::is_lvalue_reference<T>::value, "Can not forward an rvalue as an lvalue."); return static_cast<T&&>(t); }
but note that in C++14, std::forward
is constexpr
.
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