C++ 方法调用的行为意外地类似于l值

C++ 方法调用的行为意外地类似于l值,c++,C++,谁能解释一下为什么要编译: 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; } }; in

谁能解释一下为什么要编译:

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行上赋值的左操作数需要左值因为IMO
f.GetBar()
不是一个l值,但它编译起来非常简单并且
f.GetBar()=m2是一个NOP

另一方面,如果我替换
typedef longlong Foo通过
typedef long Foo,前面提到的行无法编译,我得到了预期的错误


我在重构一些旧代码时遇到了这个问题。这个问题中的代码只是为了说明这个问题。

之所以有效,是因为
longlong
是一个类,因此
=
longlong::operator=
,隐式定义的赋值运算符。您可以在临时变量上调用成员函数,只要它们不符合
&
(默认的
运算符=
是不符合条件的)

要将赋值运算符限制为左值,可以使用附加的ref限定符显式默认赋值运算符:

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

    // ...
};

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

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


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

其他答案中描述了此行为的原因

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

struct FooStruct
{
    ...
    const Foo GetBar() {return bar;}
};
您不能将值分配给
const
对象,因此编译器会抱怨

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

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


回想一下,您的
=
这里实际上是对
操作符=
的调用,这是一个函数。

删除
赋值操作符?使用
限定
操作符=
。如^@Quentin中所述,
&
后参数的含义是什么?@这意味着只有当
&
时才使用该方法@M.M明白了!:“显式默认的函数应[…]具有与隐式声明[…]相同的声明函数类型(除了可能不同的ref限定符[…])。所以这很好:)我很好奇锈菌是否存在这个问题,所以我写了一篇文章。它没有;这个例子没有编译,说明赋值是不正确的。这个问题也是,实际上它是C++的C子集(!!)的不兼容。(在链接示例中,从C到C++的切换防止了错误的发送)。
  f.GetBar() = m2;     // Here I'd expect an error such as
                       // "error: lvalue required as left operand of assignment"