【问题标题】:What is the meaning of assigning to return value of getter taking this pointer by value?将这个指针按值赋值给getter的返回值是什么意思?
【发布时间】:2020-08-05 14:04:05
【问题描述】:

当我跳入现有代码并错误地使用 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;

}

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

  • b.getAbyVal() = A{7};b.getXbyVal() = 3; 有什么不同,所以前者编译而后者不编译(除了类型是 Aint)?
  • void operator=(A const&amp; other) { x = other.x; } 更改为void operator=(A const&amp; other) &amp; { x = other.x; } 会使b.getAbyVal() = A{7}; 无法编译。为什么会这样?

【问题讨论】:

  • A&amp; getAbyRef() 中,&amp; 属于返回类型A&amp;。在void operator=(A const&amp; other) &amp; 中,它充当引用限定符,并且与返回类型无关。 &amp; 的这两种用法没有可比性。
  • void operator= 通常返回类型是对对象的引用。就个人而言,我更喜欢像您在这里一样返回void 进行分配,但这不是惯用的C++。如果您的团队愿意返回void,一切都很好。
  • @Eljay,不,我的团队不在船上,但我认为返回 void 会帮助我(以及问题的读者)减少显式参数类型之间的混淆,隐式参数 @ 987654341@,以及返回值。

标签: c++ this getter return-by-reference return-by-value


【解决方案1】:

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

令人惊讶的是,类型的不同正是导致一个编译正确而另一个编译失败的原因。

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

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

【讨论】:

  • 接受这一点是因为它阐明了分配给右值对象不是错误的,因为分配是通过 operator= 函数发生的,除了(可能但不一定)将某些东西分配给*this 操作数将在调用后立即删除。
【解决方案2】:

b.getAbyVal() = A{7};b.getXbyVal() = 3; 有什么不同,所以前者可以编译,而后者不能编译(除了类型是 Aint)?

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

42 = x; // error

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

A{} = x; // ok

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

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

A{} = x;  // now error
A a;
a = x;    // still ok

【讨论】:

  • 在表面上,42 与函数的返回值不同。出于问题的目的,我质疑这种比较的有效性。
  • 是的,我使用临时作为可能对 OP 有意义的比较。解决这个问题似乎很合理。
猜你喜欢
  • 1970-01-01
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多