Почему std:: forward возвращает static_cast <T &&>, а не static_cast <T>?

Возьмем функцию Y, которая перегружает:

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

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

Теперь определим функцию шаблона, которая действует как std:: forward

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

Теперь посмотрите на main()

int main()
{
   int i = 10;

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

Как и ожидалось, выход

lvalue!
rvalue!

Теперь вернитесь к функции шаблона f() и замените static_cast<T&&>(x) на static_cast<T>(x). Посмотрим на выход:

lvalue!
rvalue!

То же самое! Зачем? Если они совпадают, то почему std::forward<> возвращает листинг с x на T&&?

Ответы

Ответ 1

Классификация lvalue vs rvalue остается прежней, но эффект совсем другой (и категория значений меняется, хотя и не в наблюдаемом виде в вашем примере). Перейдем к четырем случаям:

template<class T>
void f(T&& x)
{
    Y(static_cast<T&&>(x));
}

template<class T>
void g(T&& x)
{
    Y(static_cast<T>(x));
}

Если мы назовем f с lvalue, T выведем в качестве некоторого X&, поэтому ссылка на литье свернется X& && ==> X&, поэтому мы получим одно значение lvalue и ничего не изменится.

Если мы назовем f с rvalue, T выведем в качестве некоторого X, так что приведение просто преобразует X в ссылку rvalue на X, поэтому оно становится rvalue (в частности, значение xvalue).

Если мы назовем g с lvalue, все те же вещи произойдут. Нет необходимости в обращении, потому что мы просто используем T == X&, но листинг все еще не работает, и мы все равно оказываемся в том же lvalue.

Но, если мы будем называть g с rvalue, мы имеем static_cast<T>(x), который будет скопировать X. Эта копия является rvalue (как проверяет ваш тест, за исключением теперь это prvalue, а не xvalue), но в лучшем случае это лишняя, ненужная копия и будет ошибкой компиляции (если T является подвижным, но не подлежащим копированию) в худшем случае. С помощью static_cast<T&&>(x) мы перешли к ссылке, которая не вызывает копию.

Итак, почему мы делаем T&&.