5

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

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

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

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

4

6 回答 6

8

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

于 2012-04-24T06:31:59.553 回答
7

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

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

C++ 03 12.8 复制类对象

第 15 段

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

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

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

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

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

于 2012-04-24T06:35:01.550 回答
3

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

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

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

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

A program is ill-formed if the copy constructor or the copy assignment operator for an object is implicitly used and the special member function is not accessible (clause 11).

So, we only get to paragraph 15 (where copying can be omitted) if the access check specified in paragraph 14 has already been passed.

于 2012-04-24T06:35:52.453 回答
2

It's the copy itself that is omitted. Whenever copying is actually done, it must be done using the copy constructor, but there are several cases where the compiler is allowed to optimize away a copy.

Larger and non-pod objects are returned by the stack. The caller prepares a space for the object and passes pointer to that space as hidden parameter. The callee than copies the object into that space. Two optimizations can be done here:

  1. The callee can create the object directly in the return space. The compiler has to be sure the object is going to be returned, that is only return statements returning that object may follow and intervening code must not throw.
  2. If the result is assigned to variable, the caller may pass it's address instead of creating a temporary. This can only be done if the variable is either being initialized or if the effect would provably not differ from calling operator=, which is basically if the type has default constructor, destructor and operator=.
于 2012-04-24T06:41:40.513 回答
1

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

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

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

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

于 2012-04-24T06:34:57.303 回答
0

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

您将看到没有调用复制 coinstructor。

于 2012-04-24T06:31:43.533 回答