【问题标题】:Can copy elision/RVO cause a copy/move from the same object可以复制省略/RVO 导致从同一对象复制/移动
【发布时间】:2018-04-11 17:49:57
【问题描述】:

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

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。但是,使用复制省略/RVO,是否有可能从obj 复制到obj

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

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

【问题讨论】:

  • 这或多或少是我得出的结论,但我对规范的理解不够好,无法得出结论性的答案。

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


【解决方案1】:

但是,使用复制省略/RVO,是否有可能从obj 复制到obj

没有。复制 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;

【讨论】:

  • 但是这样会导致UB,因为obj还没有被初始化,对吧?
  • @jpfx1342 如果您使用true 调用它,则不会,因为您将获得一个默认对象。我已经添加了当你用 false 调用它时会发生什么。
  • 为了补充答案,RVO 不会发生,因为编译器不知道将返回什么实例,该函数有两个 return 表达式。除非函数是 constexpr 并且 bool make_new 在编译时是已知的。
【解决方案2】:

复制/移动任务永远不能被省略;省略仅在对象初始化时发生。

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

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

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

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

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

同样:

SomeObject obj = copy_maybe(false, obj);

这也激怒了UB。 obj 的初始化是非空的;因此,它的生命周期在其初始化完成之前不会开始。这发生在copy_maybe。但是copy_maybe 被赋予了一个对象的引用,该对象的生命周期还没有开始。那是UB。

【讨论】:

  • 虽然我接受了另一个答案,但我认为这个答案也很有价值,因为它更详细地介绍了对象的生命周期。
【解决方案3】:

但是,使用复制省略/RVO,是否有可能从obj 复制到obj

拒绝“复制省略/RVO”。
是的“复制将从objobj”,但仅作为复制分配。

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

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

   return *this;
}

【讨论】:

    猜你喜欢
    • 2020-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 2018-09-17
    • 2014-01-02
    • 2019-05-14
    相关资源
    最近更新 更多