Подумайте об этом так, вы использовали std::forward<T>
в func
, поэтому, чтобы убедиться, что параметр передается как ссылка Rvalue, вы должны сделать то же самое в рекурсивной функции:
void testRef(int && param)
{
std::cout << "Rvalue reference" << std::endl;
// Here's the thing I can't get. Why param is lvalue reference here??
testRef( param );
testRef(std::forward<int &&>(param)); // now it will stay an Rvalue reference
testRef(std::move(param)); // make it an Rvalue reference
}
Причина, по которой нам нужны std::forward
или std::move
, заключается в том, что param
имеет тип int&&
, который является lvalue (т.е. ссылочный параметр rvalue является выражением lvalue, когда вы его используете).
За кулисами эти шаблоны в конечном итоге выполнят static_cast<int &&>
, который выдает выражение xvalue (которое также классифицируется как выражение rvalue.) Выражение xvalue связывается с ссылочными параметрами rvalue.
Это можно увидеть, посмотрев Дерево синтаксиса Clang для следующей функции:
rvalue reference parameter (which binds to rvalue expressions)
vvvvvvvvvvv
void testRef(int&& param)
{
//std::move(param);
lvalue expression of type int&&
vvvvv
static_cast<int &&>(param);
^^^^^^^^^^^^^^^^^^^^^^^^^^
xvalue expression
(considered an rvalue expression which binds to rvalue reference parameters)
}
Абстрактное дерево синтаксиса для указанной выше функции:
TranslationUnitDecl
`-FunctionDecl <line:3:1, line:7:1> line:3:6 testRef 'void (int &&)'
|-ParmVarDecl <col:14, col:21> col:21 used param 'int &&'
`-CompoundStmt <line:4:1, line:7:1>
`-CXXStaticCastExpr <line:6:5, col:30> 'int' xvalue static_cast<int &&> <NoOp>
`-DeclRefExpr <col:25> 'int' lvalue ParmVar 0x55a692bb0a90 'param' 'int &&'
Сокращенный способ объяснить, что ссылочный параметр становится lvalue, - это сказать, что когда у него есть имя (id-выражение), это lvalue.