Search code examples
c++c++11rvalue-reference

C++ rvalue reference requestion


I'm confusing with rvalue reference,
is_rvalue_reference<decltype(a)>::value indicates that the a variable is rvalue reference,
but can't pass it to hello(int &&) function.

#include <iostream>
#include <string>
#include <array>
using std::cout;
using std::endl;
using std::boolalpha;
using std::string;
using std::is_rvalue_reference;
using std::move;


void hello(int && z) {};


int main(void) {
    int && a = 20;

    // a is_xvalue: true
    cout << "a is_xvalue: " << boolalpha << is_rvalue_reference<decltype(a)>::value << endl;

    // error C2664:  
    // 'void hello(int &&)': cannot convert argument 1 from 'int' to 'int &&'
    hello(a);       

    // compile ok.
    hello(move(a));

    return 0;
}

Solution

  • a can be an identifier, but it can also be an expression. Identifiers and expressions are different things. Expressions have types and value categories, and identifiers only have types.

    1. In the line

      int&& a = 20;
      

      a is an identifier (id-expression), not an expression. It has type int&& and it has no value category. decltype(a) will return the type int&&:

      If the argument is an unparenthesized id-expression ..., then decltype yields the type of the entity named by this expression.

    2. In the line

      hello(a);
      

      a is an expression, not an identifier. Now it has a value category, which is lvalue, because a is a name of a variable (type doesn't matter):

      The following expressions are lvalue expressions:

      • the name of a variable, ... Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression; ...

      It also has a type, which is int, because the standard reads:

      If an expression initially has the type “reference to T”, the type is adjusted to T prior to any further analysis.

      To treat a as an expression in decltype, you parenthesize it: decltype((a)) will return int&, because for lvalue expressions of type T it returns T&:

      If the argument is any other expression of type T, and

      • if the value category of expression is xvalue, then decltype yields T&&;
      • if the value category of expression is lvalue, then decltype yields T&;

      int&& in hello(int&&) cannot bind to expressions with lvalue value category, compilation fails.

    3. In the line

      hello(std::move(a));
      

      std::move(a) is an expression. Its value category is xvalue:

      The following expressions are xvalue expressions:

      • a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x); ...

      Its type is also int. decltype(std::move(a)) will return int&&, because for xvalue expressions of type T it returns T&&. int&& in hello(int&&) can bind to xvalues of type int, compilation succeeds.