什么是副本和返回值优化?
What is copy elision? What is (named) return value optimization? What do they imply?
In what situations can they occur? What are limitations?
- If you were referenced to this question, you're probably looking for the introduction.
- For a technical overview, see the standard reference.
- See common cases here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
简介
有关技术概述 - 跳至此答案。
对于发生复制省略的常见情况 - 跳到此答案。
复制省略是大多数编译器实现的一种优化,以防止在某些情况下出现额外的(可能昂贵的)复制。它使得按值返回或按值传递在实践中变得可行(有限制)。
这是唯一一种消除(哈!)as-if 规则的优化形式 - 即使复制/移动对象有副作用,也可以应用复制消除。
以下示例取自 Wikipedia
:设置中,以下输出均有效:
这也意味着可以创建更少的对象,因此您也不能依赖于调用的特定数量的析构函数。您不应该在复制/移动构造函数或析构函数中包含关键逻辑,因为您不能依赖它们的调用。
如果省略对复制或移动构造函数的调用,则该构造函数必须仍然存在并且必须可访问。这确保复制省略不允许复制通常不可复制的对象,例如因为它们具有私有或删除的复制/移动构造函数。
C++17:从 C++17 开始,直接返回对象时可以保证复制消除,在这种情况下,复制或移动构造函数不需要可访问或存在:
Introduction
For a technical overview - skip to this answer.
For common cases where copy elision occurs - skip to this answer.
Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).
It's the only form of optimization that elides (ha!) the as-if rule - copy elision can be applied even if copying/moving the object has side-effects.
The following example taken from Wikipedia:
Depending on the compiler & settings, the following outputs are all valid:
This also means fewer objects can be created, so you also can't rely on a specific number of destructors being called. You shouldn't have critical logic inside copy/move-constructors or destructors, as you can't rely on them being called.
If a call to a copy or move constructor is elided, that constructor must still exist and must be accessible. This ensures that copy elision does not allow copying objects which are not normally copyable, e.g. because they have a private or deleted copy/move constructor.
C++17: As of C++17, Copy Elision is guaranteed when an object is returned directly, and in this case, the copy or move constructor need not be accessible or present:
的复制省略形式
技术概述
- 跳过此答案。对于不太技术的视图&简介 - 跳过此答案。
(命名)返回值优化是复制elision的一种常见形式。它指的是从方法返回值的对象将其副本浮出水面的情况。标准中列出的示例说明了命名返回值优化,因为该对象是命名的。
常规返回值优化在返回临时性时会发生:
发生复制责任的其他常见位置是从临时构建的对象时:
或当例外时被价值投掷和捕获:
复制省略的常见局限性为:
大多数商业级编译器支持副本Elision& (n)RVO(取决于优化设置)。 C ++ 17使上述许多副本强制性的副本都必须进行。
Common forms of copy elision
For a technical overview - skip to this answer.
For a less technical view & introduction - skip to this answer.
(Named) Return value optimization is a common form of copy elision. It refers to the situation where an object returned by value from a method has its copy elided. The example set forth in the standard illustrates named return value optimization, since the object is named.
Regular return value optimization occurs when a temporary is returned:
Other common places where copy elision takes place is when an object is constructed from a temporary:
or when an exception is thrown and caught by value:
Common limitations of copy elision are:
Most commercial-grade compilers support copy elision & (N)RVO (depending on optimization settings). C++17 makes many of the above classes of copy elision mandatory.
标准参考
技术视图较低的 &简介 - 跳过此答案。
对于发生复制责任的常见情况 - 跳过此答案。
标准中定义
复制ELISION 在: 12.8复制和移动类对象
的
给定的示例是:
并解释:
Standard reference
For a less technical view & introduction - skip to this answer.
For common cases where copy elision occurs - skip to this answer.
Copy elision is defined in the standard in:
12.8 Copying and moving class objects [class.copy]
as
The example given is:
and explained:
复制省略是一种编译器优化技术,可消除不必要的对象复制/移动。
在以下情况下,允许编译器省略复制/移动操作,从而不调用关联的构造函数:
即使复制省略发生并且复制/移动构造函数没有被调用,它必须存在并且可访问(就好像根本没有发生优化一样),否则程序是错误的。
您应该只在不会影响软件可观察行为的地方允许这种复制省略。复制省略是唯一允许具有(即省略)可观察到的副作用的优化形式。示例:
GCC 提供
-fno-elide-constructors
选项来禁用复制省略。如果您想避免可能的复制省略,请使用 -fno-elide-constructors 。
现在,几乎所有编译器在启用优化时都提供复制省略(如果没有设置其他选项来禁用它)。
结论
对于每一次复制省略,都会省略复制的一次构造和一次匹配破坏,从而节省 CPU 时间,并且不会创建一个对象,从而节省堆栈帧上的空间。
Copy elision is a compiler optimization technique that eliminates unnecessary copying/moving of objects.
In the following circumstances, a compiler is allowed to omit copy/move operations and hence not to call the associated constructor:
Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
You should permit such copy elision only in places where it won’t affect the observable behavior of your software. Copy elision is the only form of optimization permitted to have (i.e. elide) observable side-effects. Example:
GCC provides the
-fno-elide-constructors
option to disable copy elision.If you want to avoid possible copy elision, use
-fno-elide-constructors
.Now almost all compilers provide copy elision when optimisation is enabled (and if no other option is set to disable it).
Conclusion
With each copy elision, one construction and one matching destruction of the copy are omitted, thus saving CPU time, and one object is not created, thus saving space on the stack frame.
这里我给出了我今天显然遇到的另一个复制省略的例子。
结果:
Here I give another example of copy elision that I apparently encountered today.
With the result: