C ++ универсальные ссылки. Почему ссылка rvalue становится lvalue? - PullRequest
2 голосов
/ 17 марта 2019

Вот код, который меня беспокоит

‍‍‍‍‍#include <iostream>

#include "DataItem.h"


void testRef( const int & param )
{
    std::cout << "Lvalue reference" << std::endl;
}

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 );
}


template<class T>
void func( T && param )
{
    testRef( std::forward<T>( param ) );
}


int main2() 
{
    int a = 12;
    func( a );

    std::cout << "=================" << std::endl;

    func( 14 );

    std::cout << "=================" << std::endl;

    return 0;
}

Когда я вызываю testRef() в testRef( int && param ), я предполагаю, что, поскольку param является ссылкой на rvalue, будет вызвана функция ravalue (и да, произойдет вечная рекурсия). Но функция lvalue вызывается. Зачем?

1 Ответ

3 голосов
/ 17 марта 2019

Подумайте об этом так, вы использовали 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.

...