C++ 右值引用请求

C++ rvalue reference requestion

我对右值引用感到困惑,
is_rvalue_reference<decltype(a)>::value表示a变量是右值引用,
但不能将其传递给 hello(int &&) 函数。

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

Types and value categories是两个独立的东西。

Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category.

a的类型是int&&,即右值引用。但是作为命名变量,a 是一个左值,不能绑定到右值引用。这似乎令人困惑,但请尝试分别考虑它们。

(强调我的)

The following expressions are lvalue expressions:

  • the name of a variable, a function, a template parameter object (since C++20), or a data member, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;

虽然 a 看起来像是 r 值,但实际上不是。

a 本身是一个包含右值引用的左值。

可以取a的地址,但不能取move(a)的地址。

请查看此 article 以真正了解专门针对您的问题的价值类别。

a可以是标识符,也可以是表达式。标识符和表达式是不同的东西。表达式有types and value categories,标识符只有类型

  1. 在行

    int&& a = 20;
    

    a 是标识符(id-expression),不是表达式。它有类型 int&& 并且没有值类别。 decltype(a)return 类型 int&&:

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

  2. 行中

    hello(a);
    

    a 是一个表达式,不是标识符。现在它有一个值类别,它是 lvalue,因为 a 是一个变量的名称(类型无关紧要):

    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; ...

    它还有一个类型,就是int,因为标准的reads:

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

    要将 a 视为 decltype 中的表达式,请将其括起来:decltype((a)) 将 return int&,因为对于 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&&) 无法绑定到具有 lvalue 值类别的表达式,编译失败。

  3. 在行

    hello(std::move(a));
    

    std::move(a) 是一个表达式。它的值类别是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); ...

    它的类型也是intdecltype(std::move(a)) 将 return int&&,因为对于 xvalue 类型的表达式 T it returns T&& . hello(int&&)中的int&&可以绑定到int类型的xvalues,编译成功。