当 RVO/NRVO 启动时,对象是否被复制

Is the object copied or not when RVO/NRVO kicks in?

本文关键字:对象 是否 复制 RVO NRVO 启动      更新时间:2023-10-16

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

在这种情况下,实现将省略的复制操作的源和目标视为引用同一对象的两种不同方式,并且该对象的销毁发生在如果没有优化的情况下销毁这两个对象的时间较晚。

看起来省略的不是复制构造函数调用,而是副本本身 - 只是对象首先在"复制"位置构造,因此没有"原始"对象,根本没有复制。因此,即使类具有private复制构造函数,当 RVO 启动时,也可以从函数返回,因为没有副本。

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

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

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

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

C++ 03 12.8 复制类对象

第15段

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

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

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

当对象类具有私有复制构造函数时,是否应允许从函数返回对象?

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

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

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

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

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

如果隐式使用对象的复制构造函数或复制赋值运算符,并且无法访问特殊成员函数,则程序格式不正确(条款 11)。

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

略的是副本本身。每当实际完成复制时,都必须使用复制构造函数来完成,但在某些情况下,编译器可以优化副本。

较大的非 Pod 对象由堆栈返回。调用方为对象准备一个空格,并将指向该空间的指针作为隐藏参数传递。然后被调用方将对象复制到该空间中。可以在此处进行两项优化:

  1. 被调用方可以直接在返回空间中创建对象。编译器必须确保将返回对象,即仅返回返回该对象可能遵循的语句,并且不得抛出干预代码。
  2. 如果将结果分配给变量,则调用方可以传递其地址,而不是创建临时地址。这只能在变量正在初始化或效果可证明与调用 operator= 没有区别时完成,这基本上是如果类型具有默认的构造函数、析构函数和operator=
是省略了复制

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

这个想法是省略复制构造函数,尽管如果编译器不更改程序的语义,则允许编译器共享内存。

当对象类具有私有复制构造函数时,是否应允许从函数返回对象?

不,这是不允许的。"应该"很难说,但允许它会让你做各种恶作剧并打破封装。在 C++11 中,您可以通过在返回中使用构造来执行此操作{}移动构造该值。

如果你看看MSDN中透露的这篇文章NRVO

您将看到没有副本助理教师调用。