方法调用意外地像 l 值一样起作用

Method call acting unexpectedly like an l-value

本文关键字:一样 起作用 意外 调用 方法      更新时间:2023-10-16

谁能解释为什么这段代码编译:

typedef struct longlong
{
unsigned long low;
long high;
}
longlong;
typedef longlong Foo;    
struct FooStruct
{
private:
Foo bar;
public:
void SetBar(Foo m)
{
bar = m;
}
Foo GetBar()
{
return bar;
}
};
int main()
{
FooStruct f;
Foo m1 = { 1,1 };
Foo m2 = { 2,2 };
f.SetBar(m1);
f.GetBar() = m2;     // Here I'd expect an error such as
// "error: lvalue required as left operand of assignment"
}

我预计编译会因在线f.GetBar() = m2;error: lvalue required as left operand of assignment而失败,因为 IMOf.GetBar()不是 l 值,但它编译得无形,f.GetBar() = m2;是 NOP。

另一方面,如果我将typedef longlong Foo;替换为typedef long Foo;,前面提到的行将无法编译,并且我得到预期的错误。

我在重构一些旧代码时遇到了这个问题。此问题中的代码除了说明此问题外没有任何目的。

这是有效的,因为longlong是一个类,因此=是隐式定义的赋值运算符longlong::operator =。你可以调用临时成员函数,只要它们不是用&限定的(默认operator =是非限定的)。

若要将赋值运算符限制为左值,可以使用其他 ref 限定符显式默认它:

struct longlong
{
longlong &operator = (longlong const &) & = default;
//                                      ^
// ...
};

在类类型的对象上使用运算符意味着调用函数(左侧操作数的成员函数,或将左侧操作数作为第一个参数的自由函数)。这称为运算符重载

可以调用右值上的函数,这样就不会出现编译错误。

在实现重载赋值运算符时,您可以对其进行标记,以便不能在右值上调用它,但是您使用的任何类的设计者都选择不这样做。

f.GetBar() = m2;     // Here I'd expect an error such as
// "error: lvalue required as left operand of assignment"

你的期望是错误的。此规则仅适用于内置类型的对象。

[C++14: 3.10/5]:为了修改对象,对象的左值是必需的,但在某些情况下,类类型的右值也可用于修改其引用。[ 示例:为对象 (9.3) 调用的成员函数可以修改该对象。—结束示例 ]

回想一下,您在此处=实际上是对operator=的调用,这是一个函数。

其他答案中描述了这种行为的原因。

避免此行为的一种方法是使用const限定返回类型:

struct FooStruct
{
...
const Foo GetBar() {return bar;}
};

不能为const对象赋值,因此编译器会抱怨。