The implementation of std::forward
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?
Solution 1:
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;
Solution 2:
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
.
Solution 3:
The first case as Sebastian Redl said will always give you an lvalue reference. The reason is that an rvalue reference in the parameter would be passed as an lvalue reference, and the parameter T&&
type is a universal reference rather than an rvalue reference.
Actually if the first case is correct, we don't even need forward
any more. Here is an experiment to demonstrate how universal reference parameters are passed
template <typename T, typename U>
void g(T&& t, U&& u)
{
std::cout << "t is lvalue ref: "
<< std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1
std::cout << "t is rvalue ref: "
<< std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0
std::cout << "u is lvalue ref: "
<< std::is_lvalue_reference<decltype(u)>::value << std::endl; // 1
std::cout << "u is rvalue ref: "
<< std::is_rvalue_reference<decltype(u)>::value << std::endl; // 0
}
template <typename T, typename U>
void f(T&& t, U&& u)
{
std::cout << "t is lvalue ref: "
<< std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1
std::cout << "t is rvalue ref: "
<< std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0
std::cout << "u is lvalue ref: "
<< std::is_lvalue_reference<decltype(u)>::value << std::endl; // 0
std::cout << "u is rvalue ref: "
<< std::is_rvalue_reference<decltype(u)>::value << std::endl; // 1
g(t, u);
}
int main()
{
std::unique_ptr<int> t;
f(t, std::unique_ptr<int>());
return 0;
}
The program turns out that both t
and u
passed from f
to g
is lvalue references, despite that u
is an rvalue reference in f
. So in the first case the parameter of forward
just doesn't have a chance to be an rvalue reference.
The identity
is used to change the parameter type from universal reference to an rvalue reference (as mentioned by Redl, it's more precise to use std::remove_reference
). However this change makes the template type deduction not possible any longer, so that the type parameter for forward
is mandatory, as a result we shall write forward<T>(t)
.
But the second case in your question is not correct either, as also mentioned by Redl, the correct approach is an overload whose parameter is an lvalue reference.
The most straightforward implementation I can find is this
template <typename T>
T&& forward(typename identity<T>::type& param)
{
return static_cast<T&&>(param);
}
It works for universal references, for example
template <typename T, typename U>
void f(T&& t, U&& u)
{
::forward<T>(t);
::forward<U>(u);
}
std::unique_ptr<int> t;
f(t, std::unique_ptr<int>());
// deduction in f:
// T = unique_ptr&, decltype(t) = unique_ptr&
// U = unique_ptr, decltype(u) = unique_ptr&& (but treated as an lvalue reference)
// specialization of forward:
// forward<T> = forward<unique_ptr&>, param type = unique_ptr&
// return type = unique_ptr&
// forward<U> = forward<unique_ptr>, param type = unique_ptr&
// return type = unique_ptr&&