我正在阅读有关 C++ 右值引用和表达式的值类别的内容。但是,我无法理解(从左值、x值、纯右值的角度)为什么以下代码按预期工作:
struct S {
int a;
};
S&& t(S& a) {
return (S&&)a;
}
int main()
{
S s = { 1 };
t(s) = S{ 2 };
cout << s.a; // prints 2
return 0;
}
而下面的代码甚至无法编译,并显示
error: using rvalue as lvalue
:
int&& t(int& a) {
return (int&&)(a);
}
int main()
{
int s = 1;
t(s) = 2; // error
std::cout << s;
return 0;
}
对我来说,在这两种情况下
t(s)
都应该表现为 xvalue
。因此,它可以出现在赋值运算符的左侧(在此上下文中不讨论运算符重写)。标准字面意思是:调用返回类型为右值引用的函数的结果是 xvalue。为什么 int
和 struct S
的行为不同?标准(或 cppreference.com)是否以任何方式预测了这种行为?我无法意识到这个场景中的想法顺序是什么。
我期待第二个代码打印
2
,因为最初保存s
的内存位置1
将通过2
被rvalue reference
覆盖。
正如标准所指出的:
[注 1:...例如,内置赋值运算符期望左操作数是左值并且右操作数是纯右值并产生左值作为结果。 用户定义的运算符是函数,它们期望和产生的值的类别由它们的参数和返回类型决定。 — 尾注]
也就是说,
operator=
使用的S
实际上是编译器隐式生成的函数。所以,默认的就像:
S& operator=(const S&) { /*...*/ return *this; }
S& operator=(S&&){ /*...*/ return *this; }
这将允许用户调用该函数,其行为与内置运算符不同;但是,如果您通过引用限定符显式禁用它:
S& operator=(const S&) & { /*...*/ return *this; }
S& operator=(S&&) & { /*...*/ return *this; }
// Or e.g. S& operator=(S&&) & =default;
那么该函数只能用于左值。