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

Can copy elision/RVO cause a copy/move from the same object

假设我有一个如下所示的函数:

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

我这样称呼它:

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

如果没有复制省略,这显然总是会从 copy_maybe 中创建的临时文件复制到 obj。但是,使用复制 elision/RVO,是否可能从 obj 复制到 obj

更具体地说,在这些(或类似)条件下,在复制运算符 (void operator=(SomeObject const &other)) 中,由于复制省略,this&other 是否可能相同?

我在 Ideone 上创建了一个 test,它 returns 单独的地址,但我只是想确保此行为由规范定义。

However, with copy elision/RVO, is it possible that the copy will happen from obj to obj?

否"copy elision/RVO"。
对 "copy will happen from obj to obj" 是,但仅作为复制作业。

建议检查用户定义的复制赋值函数中的自赋值。当您这样做时,您的代码应该可以正常工作。

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

   return *this;
}

However, with copy elision/RVO, is it possible that the copy will happen from obj to obj?

没有。 Copy elison/RVO 用于初始化一个变量的过程。由于您已经使用 SomeObject obj; 初始化了 obj,因此您不会得到任何优化。复制赋值运算符将被调用,调用站点的 obj 将被分配函数中 obj 的值。

如果你有

SomeObject obj = copy_maybe(true, obj);

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


请注意调用

SomeObject obj = copy_maybe(false, obj);

将使 obj 处于不确定状态,因为它与

相同
SomeObject obj = obj;

Copy/move assignments 永远不能省略;省略仅在对象初始化时发生。

但是,有一种方法可以使省略应用于现有对象:

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

C++17 以一种有趣的方式定义了这一点。放置 new 发生在构建新对象之前。该对象的放置 new 调用被称为 "obtain storage"。

并且标准规定,当您对一个对象 "obtain storage" 时,该存储中已经存在的任何对象的生命周期都将终止(因为它们的存储被重新用于新对象)。因此,最初声明的对象的生命周期结束了。但是它的 destructor 没有被调用;如果您的程序依赖于在对象生命周期结束之前调用的析构函数,那么您就有了 UB。

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

同理:

SomeObject obj = copy_maybe(false, obj);

这也招惹了UB。 obj的初始化为non-vacuuous;因此,它的生命周期直到它的初始化完成才开始。这发生在 copy_maybe。但是 copy_maybe 被赋予了对尚未开始生命周期的对象的引用。那是UB。