gedamial gedamial - 4 months ago 22
C++ Question

Why does std::forward return static_cast<T&&> and not static_cast<T>?

Let's have a function called Y that overloads:

void Y(int& lvalue)
{ cout << "lvalue!" << endl; }

void Y(int&& rvalue)
{ cout << "rvalue!" << endl; }

Now, let's define a template function that acts like std::forward

template<class T>
void f(T&& x)
Y( static_cast<T&&>(x) ); // Using static_cast<T&&>(x) like in std::forward

Now look at the main()

int main()
int i = 10;

f(i); // lvalue >> T = int&
f(10); // rvalue >> T = int&&

As expected, the output is


Now come back to the template function
and replace
. Let's see the output:


It's the same! Why? If they are the same, then why
returns a cast from


The lvalue vs rvalue classification remains the same, but the effect is quite different (and the value category does change - although not in an observable way in your example). Let's go over the four cases:

template<class T>
void f(T&& x)

template<class T>
void g(T&& x)

If we call f with an lvalue, T will deduce as some X&, so the cast reference collapses X& && ==> X&, so we end up with the same lvalue and nothing changes.

If we call f with an rvalue, T will deduce as some X so the cast just converts x to an rvalue reference to x, so it becomes an rvalue (specifically, an xvalue).

If we call g with an lvalue, all the same things happen. There's no reference collapsing necessary, since we're just using T == X&, but the cast is still a no-op and we still end up with the same lvalue.

But if we call g with an rvalue, we have static_cast<T>(x) which will copy x. That copy is an rvalue (as your test verifies - except now it's a prvalue instead of an xvalue), but it's an extra, unnecessary copy at best and would be a compilation failure (if T is movable but noncopyable) at worst. With static_cast<T&&>(x), we were casting to a reference, which doesn't invoke a copy.

So that's why we do T&&.