I've understood how std::move
works and implemented my own version for practice only. Now I'm trying to understand how std::forward
works:
I've implemented this so far:
#include <iostream>
template <typename T>
T&& forward_(T&& x)
{
return static_cast<T&&>(x);
}
/*template <typename T>
T&& forward_(T& x)
{
return static_cast<T&&>(x);
}*/
void incr(int& i)
{
++i;
}
void incr2(int x)
{
++x;
}
void incr3(int&& x)
{
++x;
}
template <typename T, typename F>
void call(T&& a, F func)
{
func(forward_<T>(a));
}
int main()
{
int i = 10;
std::cout << i << '\n';
call(i, incr);
std::cout << i << '\n';
call(i, incr2);
std::cout << i << '\n';
call(0, incr3); // Error: cannot bind rvalue reference of type int&& to lvalue of type int.
std::cout << "\ndone!\n";
}
Why must I provide the overloaded forward(T&)
version taking an lvalue reference? As I understand it a forwarding reference can yield an lvalue or an rvalue depending on the type of its argument. So passing the prvalue literal 0
to call
along with the incr3
function that takes an rvalue reference of type int&&
normally doesn't need forward<T>(T&)
?!
If I un-comment the
forward_(T&)
version it works fine!?I'm still confused about: why if I only use the
forward_(T&)
version does it work for any value category? Then what is the point in having the one taking a forwarding referenceforward_(T&&)
?If I un-comment the version taking lvalue reference to
T&
and the one taking forwarding referenceT&&
then the code works fine and I've added some messages inside both to check which one called. the result is the the one withT&&
never called!template <typename T> T&& forward_(T& x) { std::cout << "forward_(T&)\n"; return static_cast<T&&>(x); } template <typename T> T&& forward_(T&& x) { std::cout << "forward_(T&&)\n"; return static_cast<T&&>(x); }
I mean running the same code in the driver program I've shown above.