右值

rvalue 表达式是可以隐式移出的任何表达式,无论它是否具有标识。

更准确地说,rvalue 表达式可以用作函数的参数,该函数接受 T && 类型的参数(其中 Texpr 的类型)。只有 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