可以复制elision / RVO导致从同一个对象

时间:2018-04-11 17:49:57

标签: c++ copy-elision return-value-optimization

假设我有一个看起来像这样的函数:

SomeObject copy_maybe(bool make_new, const SomeObject& def)
{
    if (make_new)
        return SomeObject();
    else
        return def;
}

我称之为:

SomeObject obj;
obj = copy_maybe(true, obj);

如果没有复制省略,这显然会导致从obj中创建的临时复制到copy_maybe。但是,使用copy elision / RVO,副本是否可能从obj发送到obj

更具体地说,在这些(或类似)条件下,复制操作符(void operator=(SomeObject const &other))是否有可能this&other由于复制省略而相同?< / p>

我在Ideone上创建了一个test,它返回了不同的地址,但我只是想确保这个行为是由规范定义的。

3 个答案:

答案 0 :(得分:4)

  

但是,对于copy elision / RVO,副本是否可能从obj发送到obj

没有。复制elison / RVO用于初始化变量的过程。由于您已使用obj初始化SomeObject obj;,因此您无法进行任何优化。将调用复制赋值运算符,并且将从函数中为调用站点的obj分配obj的值。

如果你有

SomeObject obj = copy_maybe(true, obj);

然后是的,复制elison可以(将在C ++ 17中)发挥作用。

请注意调用

SomeObject obj = copy_maybe(false, obj);

obj置于不确定状态,因为它与

相同
SomeObject obj = obj;

答案 1 :(得分:2)

复制/移动作业永远不会被遗漏; elision仅在对象初始化时发生。

但是,有一种方法可以将elision应用于现有对象:

SomeObject obj;
new(&obj) auto(copy_maybe(false, obj);

C ++ 17以一种有趣的方式定义了它。展示位置new在构造新对象之前发生。对于该对象,放置new调用被称为“获取存储”。

标准规定,当您为对象“获取存储空间”时,已终止该存储中任何对象的生命周期(因为它们的存储被重新用于新对象)。因此,最初声明的对象的生命周期结束。但它的析构函数没有被调用;如果你的程序依赖于在对象的生命周期结束之前调用析构函数,那么你就有了UB。

但这无论如何都会激怒UB。为什么?因为obj的生命周期在 copy_maybe被调用之前结束了。因此copy_maybe将获得对不再存在的对象的引用。当copy_maybe访问对不存在的对象的引用时,您将获得UB。

类似地:

SomeObject obj = copy_maybe(false, obj);

这也激怒了UB。 obj的初始化是非空的;因此,它的寿命直到初始化完成才开始。这发生在copy_maybe。但是copy_maybe被赋予对没有生命周期开始的对象的引用。那是UB。

答案 2 :(得分:1)

  

但是,对于copy elision / RVO,副本是否可能从obj发送到obj

不要“复制elision / RVO” 是的“将从obj复制到obj”,但仅作为复制作业。

建议在用户定义的复制分配功能中检查自我分配。当你这样做时,你的代码应该可以正常工作。

SomeObject& operator=(SomeObject const& rhs)
{
   // Do nothing for self assignment.
   if ( this != &rhs )
   {
      ...
   }

   return *this;
}