As stated at cplusplus.com, std::forward
has two signatures:
template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;
The typical use of std::forward
is to preserve rvalueness while passing arguments to other functions. Let's illustrate this with an example:
void overloaded(int &) { std::cout << "lvalue"; }
void overloaded(int &&) { std::cout << "rvalue"; }
template <typename T>
void fwd(T && t)
{
overloaded(std::forward<T>(t));
}
When we call fwd(0)
, T
deduces to int
(t
has type int &&
). Then we call std::forward<int>(t)
. The result of that call is expression of type int &&
so the second version of overloaded
function is selected and the program prints "rvalue" to the standard output.
When we call fwd(i)
(where i is some int variable), T
deduces to int&
(t
has type int &
). Then we call std::forward<int&>(t)
. The result of that call (after applying reference collapsing rules) is expression of type int &
so the first version of overloaded
function is selected and the program prints "lvalue" to the standard output.
In both these cases we use the first overload of std::forward
(the one taking typename remove_reference<T>::type& arg
). It is because even if the type of t
is int &&
it binds to lvalue reference (because named variable of type "rvalue reference to something" is itself lvalue and lvalues can't bind to rvalue reference).
Question 1:
What is that second overload of std::forward
for? Can you think of some practical example, that uses the overload taking arg
by rvalue reference?
Question 2:
cplusplus.com says:
Both signatures return the same as:
static_cast<decltype(arg)&&>(arg)
The problem I have with that, is that I'm pretty sure that it is wrong. When we try to return this from the first overload of std::forward
we get a compilation error.
When fwd
is called with int
rvalue, it calls the first overload of std::forward
with T = int
. Then, decltype(arg)
will become int&
so static_cast<decltype(arg)&&>(arg)
will collapse to static_cast<int&>(arg)
. But the return type is int &&
and we get compilation error:
cannot bind ‘std::remove_reference<int>::type {aka int}’ lvalue to ‘int&&’
Both overloaded versions of std::forward
should return static_cast<T&&>(arg)
. Am I right?
Do you think that the quote from cplusplus.com is a mistake?
class Foo {};
auto f = [](){ return Foo{}; };
auto g = []()->Foo&{ static Foo x; return x; };
template<class T>
std::false_type is_rvalue( T& ) { return {}; }
template<class T>
std::true_type is_rvalue( T&& ) { return {}; }
template<class T, class F>
auto test( F&& f ) {
return is_rvalue( std::forward<T>( f() ) );
}
int main() {
std::cout << test<Foo>(f) << "," << test<Foo&>(g) << "\n";
}
It is a bit contrived; but imagine where you have some factory that might produce a Foo
or a Foo&
depending on some unimportant details, but you know you want to forward it based on a second type T
to T&&
. You know that if T
is not a reference, then it won't produce Foo
, but if T
is a reference it might.
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