"Perfect forwarding" is used in templates, i.e.:
template<typename T>
void func(T && arg)
{
func2(std::forward<T>(arg));
}
Outside of template context the end result drastically changes. All that std::forward
does is return static_cast<T &&>
, so your call
function becomes nothing more than:
void call(int& other) {
check(static_cast<int &&>(other));
}
Hence you get an rvalue. The reason this works differently in templates is because a &&
template parameter is a forwarding reference (a fancy term for either an lvalue or an rvalue-deduced reference, depending on what gets dropped in that parameter), and because of reference collapsing rules. Briefly, when used in a template context, the end result is:
T
gets deduced as either an lvalue or an rvalue reference, depending on what the parameter is.
The result of the static_cast<T &&>
is an lvalue reference, if T
is an lvalue reference, or an rvalue reference if T
is an rvalue reference, due to reference collapsing rules.
The end result is that the same kind of a reference gets forwarded. But this only works in template context, since it requires both forwarding reference semantics and reference collapsing rules to work just right.