当我跳转到一个现有代码并错误地使用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;
不同,因此前者可以编译,而后者不可以编译(除了类型是A
和int
)?void operator=(A const&other){x=other.x;}
更改为void operator=(A const&other)& {x=other.x;}
使b.GetabyVal()=A{7};
无法编译。 为什么会出现这种情况?
在B.getabyval()=A{7}中是什么; 与b.getXByVal()=3不同; 以便前者编译而后者不编译(除了类型是A和int之外)?
令人惊讶的是,类型的差异正是使一个编译正确,而另一个编译失败的原因。
a
有一个为它定义的赋值运算符,因此编译器会在返回值上尽职地调用它(只是后来丢弃了整个对象)。 但您编写的代码支持这一点。 从编译器视图来看,在赋值运算符中可能发生了一些其他有趣的事情,尽管对象将被根除(正式的说法是副作用)。
使用int
作为返回值,编译器知道将值赋给int没有副作用,因此将任何值赋给要立即根除的对象是没有任何意义的。
b.getAbyVal()=A{7};
中的什么不同于b.getXByVal()=3;
,因此前者可以编译,而后者不可以编译(除了类型是A
和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};
无法编译。 为什么会出现这种情况呢?
在末尾添加&
称为ref-qualifier,它允许用户定义的类具有与POD类型相同的语义。 在operator=
末尾添加一个&
将限制它只能用于L值(基本上是命名变量或从函数返回的引用)。
A{} = x; // now error
A a;
a = x; // still ok