Ответ 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&&
.