右值
rvalue 表达式是可以隐式移出的任何表达式,无论它是否具有标识。
更准确地说,rvalue 表达式可以用作函数的参数,该函数接受 T &&
类型的参数(其中 T
是 expr
的类型)。只有 rvalue 表达式可以作为这些函数参数的参数给出; 如果使用非右值表达式,则重载决策将选择不使用右值引用参数的任何函数。如果不存在,则会出现错误。
rvalue 表达式的类别包括所有 xvalue 和 prvalue 表达式,并且只包含那些表达式。
标准库函数 std::move
用于将非右值表达式显式转换为右值。更具体地说,它将表达式转换为 xvalue,因为即使它之前是无标识的 prvalue 表达式,通过将其作为参数传递给 std::move
,它获得了 identity(函数的参数名称)并变为 xvalue。
考虑以下:
std::string str("init"); //1
std::string test1(str); //2
std::string test2(std::move(str)); //3
str = std::string("new value"); //4
std::string &&str_ref = std::move(str); //5
std::string test3(str_ref); //6
std::string
有一个构造函数,它接受 std::string&&
类型的单个参数,通常称为移动构造函数。但是,表达式 str
的值类别不是右值(特别是它是左值),因此它不能调用该构造函数重载。相反,它调用 const std::string&
重载,复制构造函数。
第 3 行改变了一切。std::move
的返回值是 T&&
,其中 T
是传入的参数的基本类型。所以 std::move(str)
返回 std::string&&
。函数调用谁的返回值是右值引用是一个右值表达式(特别是一个 xvalue),因此它可以调用 std::string
的移动构造函数。在第 3 行之后,str
已被移除(现在的内容未定义)。
第 4 行将临时文件传递给 std::string
的赋值运算符。这有一个过载,需要一个 std::string&&
。表达式 std::string("new value")
是一个 rvalue 表达式(特别是一个 prvalue),因此它可以调用该重载。因此,临时移动到 str
,用特定内容替换未定义的内容。
第 5 行创建一个名为 str_ref
的命名右值引用,引用 str
。这是价值类别令人困惑的地方。
请参阅,虽然 str_ref
是对 std::string
的右值引用,但表达式 str_ref
的值类别不是右值。这是一个左值表达式。对真的。因此,不能用表达 str_ref
来调用 std::string
的 move 构造函数。第 6 行因此拷贝的 str
值存入 test3
。
为了移动它,我们将不得不再次使用 std::move
。