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

Jab*_*cky 10 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;
  }
};

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"
}
Run Code Online (Sandbox Code Playgroud)

我预计编译失败的原因与error: lvalue required as left operand of assignment上线f.GetBar() = m2;,因为IMO f.GetBar()是不是一个左值,但它无缝地编译和f.GetBar() = m2;是NOP.

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

我在重构一些旧代码时遇到了这个问题.除了说明这个问题之外,这个问题中的代码没有其他目的.

Que*_*tin 12

这是有效的,因为它longlong是一个类,因此=longlong::operator =隐式定义的赋值运算符.并且您可以在临时对象上调用成员函数,只要它们不合格&(默认operator =为不合格).

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

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

    // ...
};
Run Code Online (Sandbox Code Playgroud)

  • @MM搞定了![dcl.fct.def.default](http://eel.is/c++draft/dcl.fct.def.default):"明确默认的函数应具有相同的声明函数类型(**除了可能不同的ref-qualifiers**[...]),好像它已被隐式声明[.]".所以这很好:) (4认同)
  • @VINOTHENERGETIC这意味着只有当`this`是`&`时才使用该方法./sf/ask/1964674421/ (2认同)

M.M*_*M.M 5

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

在rvalues上调用函数是很好的,因此没有编译错误.

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


Lig*_*ica 5

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

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

[C++14: 3.10/5]:为了修改对象,对象的左值是必要的,除了在某些情况下也可以使用类类型的右值来修改它的指示对象.[示例:调用对象(9.3)的成员函数可以修改对象. - 末端的例子]

回想一下,你=的实际上是一个调用operator=,这是一个函数.


ana*_*lyg 4

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

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

struct FooStruct
{
    ...
    const Foo GetBar() {return bar;}
};
Run Code Online (Sandbox Code Playgroud)

你不能给对象赋值const,所以编译器会抱怨。