10

考虑下面的最小示例:

#include<utility>

struct S { };

int main() {
    S s;
    std::move(s) = S{};
}

它编译没有错误。
如果我改用非类类型,则会出现错误。
例如,下面的代码无法编译:

#include<utility>

int main() {
    int i;
    std::move(i) = 42;
}

枚举、范围枚举等也会发生同样的情况。
错误(来自 GCC)是:

使用 xvalue(右值引用)作为左值

这背后的原因是什么?

我想这是对的,但我想了解为什么我可以对除非类之外的所有类型执行此操作。

4

2 回答 2

5

C++ 允许赋值给类对象右值,但不允许原始类型右值;

一个例子,

string s1, s2;
s1 + s2 = "asdf"; // ok since rvalue s1 + s2 is an object

int i1, i2;
i1 + i2 = 10;  // error, since i1 + i2 is a primitive type

同样的规则适用于你的问题。std::move(s) 返回对象类型的右值,但 std::move(i) 返回原始类型的右值。

于 2016-05-05T07:42:43.753 回答
1

我试图用一堆标准链接来回答我自己的问题。
我很确定我会写一些非常错误的东西,并且有人会和我一起写字。
好吧,我尽力解释了如何从标准中推断出问题中描述的内容。
如果需要,请随意投反对票,但请让我知道出了什么问题,以便能够修复答案并理解错误。
谢谢你。


3.9/8(类型):

对象类型是(可能是 cv 限定的  )类型,它不是函数类型,不是引用类型,也不是 cv void。

5.2.2/10(表达式、函数调用):

如果结果类型是对对象类型的右值引用,则函数调用是 [...]

因此在任何一种情况下std::move都是一个xvalue表达式。

5.18/3(作业):

如果左操作数不是类类型,则表达式被隐式转换 [...] 为左操作数的 cv 非限定类型。

这不会添加有用的信息,但这是为了完整性。

4.1/2(左值到右值转换):

否则,如果 T 具有类类型,则转换从泛左值复制初始化 T 类型的临时,并且转换的结果是临时的纯右值。

否则,glvalue指示的对象中包含的值就是prvalue结果。

12.2(临时对象)完成其余的工作。

因此,正如@xaxxon 在评论中提到的那样,我实际上是在尝试做(让我写)42 = 0;,它不是 C++ 中的有效表达式。

正如@bogdan 在评论中正确指出的那样,在这种情况下要参考的标准的正确部分是5.18/1(作业):

所有这些都需要一个可修改的左值作为它们的左操作数 [...]

虽然5/25/3阐明该语句仅适用于内置运算符。

于 2016-05-05T16:47:44.327 回答