复制elision和临时绑定的ref对象

时间:2013-04-08 08:13:12

标签: c++ c++11

C ++草案规定:

  

12.8p31在下列情况下(允许合并以消除多份副本),允许复制/移动操作(称为复制省略)的省略:

     

(...)

     
      
  • 尚未绑定到引用(12.2)的临时类对象将被复制/移动时   对于具有相同cv-unqualified类型的类对象,可以省略复制/移动操作   将临时对象直接构造到省略的copy / move
  • 的目标中   

换句话说:

X MakeX() {
   return X(); // Copy elided
}

X MakeX() {
   const X& x = X(); // Copy not elided
   return x;
}

引用此类限制的原因是什么?

请不要关注以下示例的有效性,因为它们只是为了举例说明我没有看到临时和参考之间的差异(恕我直言)。

一方面,通过引用引用,我们允许其他对等体为同一个对象设置别名,而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. 
}

但是这种情况呢(可能是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?
}

1 个答案:

答案 0 :(得分:3)

您永远知道将省略副本。复制省略绝不是强制性的。

因此,两种情况都是UB,或者都不是。这取决于StaticStuff:send对您传入的对象的作用。如果它保留对y._xRef*y._xPtr的任何poitner /引用,则在MakeX()之后取消引用该指针/引用返回确实会导致UB,因为原始对象x是一个自动存储持续时间在MakeX()内并且其生命周期现已结束的对象。

这个UB的结果可能是“一切正常”,因为复制椭圆确实发生了,指针/引用引用了“外部堆栈框架”中的实例。然而,这纯属巧合,它仍然是UB。