C++草案规定:
12.8p31在下列情况下允许复制/移动操作的省略,称为复制省略(可以合并以消除多份复制):
(......)
- 当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-nonqualified类型的类对象时,可以通过将临时对象直接构造到目标中来省略复制/移动操作省略的复制/移动
换一种说法:
X MakeX() {
return X(); // Copy elided
}
X MakeX() {
const X& x = X(); // Copy not elided
return x;
}
Run Code Online (Sandbox Code Playgroud)
引用受限制的原因是什么?
请不要集中在下面的例子中的有效性,因为它们只是为了举例说明,我看不到临时和基准之间的差异(恕我直言).
一方面通过引用引用,我们允许其他对等体为同一个对象设置别名,而调用者则MakeX()希望它是安全和干净的.
class Y {
public:
Y(const X& x) : _xRef(x) {}
private:
const X& _xRef;
};
X MakeX() {
const X& x = X();
Y y{x};
StaticStuff::send(y);
return x; // Oops, I promised to return a clean,
// new object, but in fact it might be silently
// changed by someone else.
}
Run Code Online (Sandbox Code Playgroud)
但是这种情况怎么样(可能是UB;)):
class Y {
public:
Y(X* x) : _xPtr(x) {}
private:
X* _xRef;
};
X MakeX() {
X x;
Y y{&x}; // I'm referencing a local object but I know it will be
// copy elided so present in the outer stack frame.
StaticStuff::send(y);
return x; // Copy elided?
}
Run Code Online (Sandbox Code Playgroud)
你永远不知道副本会被删除。复制省略从来都不是强制性的。
因此,要么两种情况都是 UB,要么都不是。这取决于StaticStuff:send你传入的对象做了什么。如果它保留了对y._xRefor的任何指针/引用*y._xPtr,那么在MakeX()返回后取消引用该指针/引用确实会导致 UB,因为原始对象x是一个内部具有自动存储持续时间MakeX()及其生命周期的对象现在已经结束了。
该 UB 的结果可能是“一切正常”,因为确实发生了复制省略,并且指针/引用引用了“外部堆栈帧”中的实例。不过,这纯粹是巧合,而且还是UB。