【问题标题】:Is the object copied or not when RVO/NRVO kicks in?当 RVO/NRVO 启动时,对象是否被复制?
【发布时间】:2012-04-24 06:16:06
【问题描述】:

我无法理解 RVO(和 NRVO)的定义,因为有多个问题 like this one 在我看来,假设 RVO 省略了复制构造函数。现在根据 12.8.15

在这种情况下,实现将省略的复制操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象将具有没有优化就被销毁了。

看起来不是复制构造函数调用被省略了,而是复制本身 - 只是对象首先在“复制”位置构造,因此没有“原始”对象,根本没有复制。因此,即使一个类有 private 复制构造函数,它也可以在 RVO 启动时从函数中返回,因为没有复制。

我说对了吗?是复制本身省略还是复制构造函数调用省略?当对象类有私有拷贝构造函数时,是否应该允许从函数返回对象?

【问题讨论】:

    标签: c++ copy-constructor nrvo rvo


    【解决方案1】:

    复制省略如果优化生效,但编译器仍需检​​查复制构造函数是否可访问。否则,如果编译器(或其他编译器)决定不优化,代码将无效。

    【讨论】:

      【解决方案2】:

      是省略了复制本身还是省略了复制构造函数调用?

      复制操作本身被省略。如果您查看完整的报价,它清楚地提到了:

      C++ 03 12.8 复制类对象

      第 15 段

      当满足某些条件时,允许实现省略类对象的复制构造,即使对象的复制构造函数和/或析构函数有副作用。在这种情况下,实现将省略的复制操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象的较晚时间 没有优化就会被破坏。111)在以下情况下允许复制操作的省略(可以结合起来消除多个副本):

      ——在具有类返回类型的函数的return语句中,当表达式是与函数返回类型具有相同cv-unqualified类型的非易失性自动对象的名称时,复制操作可以通过将自动对象直接构造到函数的返回值中来省略

      ——当一个没有绑定到引用 (12.2) 的临时类对象将被复制到具有相同 cv-unqualified 类型的类对象时,可以通过构造省略复制操作将临时对象直接放入省略副本的目标中.....

      当对象类有私有拷贝构造函数时,是否应该允许从函数中返回对象?

      RVO 和 NRVO 是编译器优化,编译器允许但不保证如果特定的哑编译器无法提供这些优化会怎样?
      如果没有可访问的复制构造函数,代码将在所有不需要的此类编译器上中断。鉴于复制构造函数应该是可访问的。

      【讨论】:

        【解决方案3】:

        官方说,不,RVO/NRVO 不影响程序是否格式正确。该标准明确允许省略复制构造函数的任何副作用,但是仍然应该对复制构造函数进行访问检查。

        然而,大多数编译器不会对省略的复制构造函数进行访问检查,除非您要求尽可能严格地执行规则。我不太确定,但我似乎记得使用过一些跳过访问检查的工具,即使您确实要求严格遵守(但我记不太清了)。

        编辑(纠正我在其他一些答案中表达的误解):

        标准部分中的段落应按顺序阅读。 第一段中适用于某种情况的要求始终适用。在这种情况下,第 15 段允许省略副作用。然而,在第 14 段之前,我们看到:

        如果隐式使用对象的复制构造函数或复制赋值运算符并且特殊成员函数不可访问(第 11 条),则程序是非良构的。

        所以,我们只进入第 15 段(可以省略复制)如果第 14 段中指定的访问检查已经通过。

        【讨论】:

          【解决方案4】:

          省略的是副本本身。无论何时实际完成复制,都必须使用复制构造函数来完成,但有几种情况允许编译器优化掉一个副本。

          堆栈返回更大的非 pod 对象。调用者为对象准备一个空间并将指向该空间的指针作为隐藏参数传递。被调用者然后将对象复制到该空间中。这里可以做两个优化:

          1. 被调用者可以直接在返回空间中创建对象。编译器必须确保该对象将被返回,即只有返回该对象的返回语句可能会跟随并且干预代码不得抛出。
          2. 如果将结果分配给变量,调用者可以传递它的地址而不是创建一个临时地址。这只能在变量正在初始化或者效果与调用operator=(基本上是如果类型具有默认构造函数、析构函数和operator=)没有区别的情况下才能完成。

          【讨论】:

            【解决方案5】:

            是省略了复制本身还是省略了复制构造函数调用?

            这个想法是省略了复制构造函数,尽管编译器可以在不改变程序语义的情况下共享内存。

            当对象类有私有拷贝构造函数时,是否允许从函数返回对象?

            不,这是不允许的。 “应该”很难说,但允许它允许你做各种恶作剧并打破封装。在 C++11 中,您可以通过在 return 中使用 {} 构造并移动构造该值来做到这一点。

            【讨论】:

            • -1:误导。它是被省略的副本。必须始终使用复制构造函数来完成复制,否则一切都会崩溃。 “应该”很容易说,不,不应该,因为编译器不必优化副本,所以必须可以做到。
            【解决方案6】:

            如果你看这篇文章NRVO revealed in MSDN

            你会看到没有调用copy coinstructor。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-12-19
              • 2018-08-03
              • 2016-01-25
              • 1970-01-01
              相关资源
              最近更新 更多