0

当我跳入现有代码并错误地使用 getter 设置属性时,我产生了这个疑问,

obj.getProp() = otherProp;

而不是调用setter,

obj.setProp(otherProp);

我没有意识到错误,因为编译或运行时没有错误;该任务导致无操作。

所以我想出了下面的例子,它输出337

#include <iostream>

struct A {
    int x = 0;
    A(int x) : x(x) {}
    A(A& a) : x(a.x) {}
    void operator=(A const& other) { x = other.x; }
};

struct B {
    A a{3};
    int x{3};
    A  getAbyVal() { return a; }
    A& getAbyRef() { return a; }
    int getXbyVal() { return x; }
};

int main() {
    B b;
    std::cout << b.a.x;   // this and the other two cout print what I expect, but...
    b.getAbyVal() = A{7}; // ... I expected this to fail at compilation time in the first place...
    //b.getXbyVal() = 3;  // ... just like this fails.
    std::cout << b.a.x;
    b.getAbyRef() = A{7};
    std::cout << b.a.x;

}

所以我的问题是两个方面:

  • inb.getAbyVal() = A{7};有什么不同b.getXbyVal() = 3;使得前者编译而后者不编译(除了类型是Aand的事实之外int)?
  • 更改void operator=(A const& other) { x = other.x; }void operator=(A const& other) & { x = other.x; }使b.getAbyVal() = A{7};编译失败。为什么会这样?
4

2 回答 2

1

inb.getAbyVal() = A{7};有什么不同b.getXbyVal() = 3;使得前者编译而后者不编译(除了类型是Aand的事实之外int)?

区别恰恰在于一个函数返回一个类类型,而另一个函数返回一个 POD 类型。临时int的,例如不能分配给:

42 = x; // error

int因此,类似地,该语言也不允许分配给从函数返回的临时值。这不是用户定义的类类型的默认行为,因此分配给临时A编译:

A{} = x; // ok

更改void operator=(A const& other) { x = other.x; }void operator=(A const& other) & { x = other.x; }使b.getAbyVal() = A{7};编译失败。为什么会这样?

在末尾添加 a&称为引用限定符,它允许用户定义的类在分配给临时对象时具有与 POD 类型相同的语义。&在末尾添加 aoperator=会将其限制为仅用于左值(基本上是命名变量或从函数返回的引用)。

A{} = x;  // now error
A a;
a = x;    // still ok
于 2020-08-05T14:11:59.820 回答
1

b.getAbyVal() = A{7}; 不同于 b.getXbyVal() = 3; 以便前者编译而后者不编译(除了类型是 A 和 int 的事实)?

令人惊讶的是,类型的差异正是使一个编译正确而另一个编译失败的原因。

A为其定义了一个赋值运算符,因此编译器尽职尽责地在返回值上调用它(仅在以后丢弃整个对象)。但是您编写的代码支持这一点。从编译器的角度来看,尽管对象将被根除(正式用语中的副作用),但赋值运算符中可能还发生了一些其他有趣的事情。

作为int返回值,编译器知道将值分配给 int 没有副作用,因此将任何值分配给要立即根除的对象没有任何意义。

于 2020-08-05T14:13:58.880 回答