本文翻译自:What are copy elision and return value optimization?

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 . 在这里查看常见情况 。

#1楼

参考:https://stackoom.com/question/sLhP/什么是复制省略和返回值优化


#2楼

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 . 这是唯一可以避免(ha!)优化规则的形式, 即使复制/移动对象有副作用,也可以应用按规则复制省略

The following example taken from Wikipedia : 以下示例摘自Wikipedia :

struct C {C() {}C(const C&) { std::cout << "A copy was made.\n"; }
};C f() {return C();
}int main() {std::cout << "Hello World!\n";C obj = f();
}

Depending on the compiler & settings, the following outputs are all valid : 根据编译器和设置,以下输出均有效 :

Hello World! 你好,世界!
A copy was made. 复制了。
A copy was made. 复制了。


Hello World! 你好,世界!
A copy was made. 复制了。


Hello World! 你好,世界!

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, eg 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: C ++ 17 :从C ++ 17开始,当直接返回对象时,可以保证复制消除:

struct C {C() {}C(const C&) { std::cout << "A copy was made.\n"; }
};C f() {return C(); //Definitely performs copy elision
}
C g() {C c;return c; //Maybe performs copy elision
}int main() {std::cout << "Hello World!\n";C obj = f(); //Copy constructor isn't called
}

#3楼

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] 12.8复制和移动类对象[class.copy]

as

31) When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. 31)当满足某些条件时,即使对象的复制/移动构造函数和/或析构函数具有副作用,也允许实现忽略类对象的复制/移动构造。 In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. 在这种情况下,实现将忽略的复制/移动操作的源和目标视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象本来应该以较晚的时间发生。没有优化就销毁。 123 This elision of copy/move operations, called copy elision , is permitted in the following circumstances (which may be combined to eliminate multiple copies): 123在以下情况下允许复制/移动操作的这种省略,称为复制删除(可以合并以消除多个副本):

— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value —在具有类返回类型的函数的返回语句中,当表达式是具有与函数返回类型相同的cvunqualified类型的非易失性自动对象(函数或catch子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object —在throw-expression中,当操作数是非易失性自动对象(函数或catch子句参数除外)的名称时,其范围不会超出最里面的try-block的末尾(如果存在)一种),可以通过将自动对象直接构造到异常对象中来省略从操作数到异常对象(15.1)的复制/移动操作

— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move —当尚未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-unqualtype类型的类对象时,可以通过将临时对象直接构造为以下形式来省略复制/移动操作:省略复制/移动的目标

— when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration. —当异常处理程序的异常声明(第15条)声明与异常对象(15.1)相同类型的对象(cv限定除外)时,可以通过处理异常声明来省略复制/移动操作如果程序的含义没有改变,则将其作为异常对象的别名,但对于由异常声明所声明的对象执行构造函数和析构函数除外。

123) Because only one object is destroyed instead of two, and one copy/move constructor is not executed, there is still one object destroyed for each one constructed. 123)因为仅破坏了一个对象而不是两个,并且没有执行一个复制/移动构造函数,所以对于每个构造对象,仍然有一个对象被破坏。

The example given is: 给出的示例是:

class Thing {
public:Thing();~Thing();Thing(const Thing&);
};
Thing f() {Thing t;return t;
}
Thing t2 = f();

and explained: 并解释:

Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing : the copying of the local automatic object t into the temporary object for the return value of function f() and the copying of that temporary object into object t2 . 在这里,可以将省略的标准组合起来,以消除对Thing类的复制构造函数的两次调用:将本地自动对象t复制到函数f()的返回值的临时对象中,以及将该临时对象复制到object中t2 Effectively, the construction of the local object t can be viewed as directly initializing the global object t2 , and that object's destruction will occur at program exit. 实际上,可以将本地对象t的构造视为直接初始化全局对象t2 ,并且该对象的破坏将在程序退出时发生。 Adding a move constructor to Thing has the same effect, but it is the move construction from the temporary object to t2 that is elided. 在Thing中添加move构造函数具有相同的效果,但是可以避免从临时对象到t2的move构造。


#4楼

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. 标准中阐述的示例说明了命名返回值优化 ,因为对象是命名的。

class Thing {
public:Thing();~Thing();Thing(const Thing&);
};
Thing f() {Thing t;return t;
}
Thing t2 = f();

Regular return value optimization occurs when a temporary is returned: 返回临时变量时,将进行常规的返回值优化

class Thing {
public:Thing();~Thing();Thing(const Thing&);
};
Thing f() {return Thing();
}
Thing t2 = f();

Other common places where copy elision takes place is when a temporary is passed by value : 发生复制省略的其他常见位置是按值传递临时值时 :

class Thing {
public:Thing();~Thing();Thing(const Thing&);
};
void foo(Thing t);foo(Thing());

or when an exception is thrown and caught by value : 抛出异常并按值捕获时 :

struct Thing{Thing();Thing(const Thing&);
};void foo() {Thing c;throw c;
}int main() {try {foo();}catch(Thing c) {  }
}

Common limitations of copy elision are: 复制省略的常见限制是:

  • multiple return points 多个返回点
  • conditional initialization 条件初始化

Most commercial-grade compilers support copy elision & (N)RVO (depending on optimization settings). 大多数商业级编译器都支持复制省略和(N)RVO(取决于优化设置)。


#5楼

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: 在以下情况下,允许编译器省略复制/移动操作,因此不调用关联的构造函数:

  1. NRVO (Named Return Value Optimization) : If a function returns a class type by value and the return statement's expression is the name of a non-volatile object with automatic storage duration (which isn't a function parameter), then the copy/move that would be performed by a non-optimising compiler can be omitted. NRVO(命名返回值优化) :如果函数按值返回类类型,并且return语句的表达式是具有自动存储期限的非易失性对象的名称(不是函数参数),则复制/移动由非优化编译器执行的操作可以省略。 If so, the returned value is constructed directly in the storage to which the function's return value would otherwise be moved or copied. 如果是这样,则将直接在函数中将返回值移动或复制到的存储器中构造返回值。
  2. RVO (Return Value Optimization) : If the function returns a nameless temporary object that would be moved or copied into the destination by a naive compiler, the copy or move can be omitted as per 1. RVO(返回值优化) :如果该函数返回一个无名临时对象,该对象将由天真的编译器移动或复制到目标中,则可以按照1忽略复制或移动。
#include <iostream>
using namespace std;class ABC
{
public:   const char *a;  ABC()  { cout<<"Constructor"<<endl; }  ABC(const char *ptr)  { cout<<"Constructor"<<endl; }  ABC(ABC  &obj)  { cout<<"copy constructor"<<endl;}  ABC(ABC&& obj)  { cout<<"Move constructor"<<endl; }  ~ABC()  { cout<<"Destructor"<<endl; }
};ABC fun123()
{ ABC obj; return obj; }  ABC xyz123()
{  return ABC(); }  int main()
{  ABC abc;  ABC obj1(fun123());//NRVO  ABC obj2(xyz123());//NRVO  ABC xyz = "Stack Overflow";//RVO  return 0;
}**Output without -fno-elide-constructors**
root@ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Constructor
Constructor
Destructor
Destructor
Destructor
Destructor  **Output with -fno-elide-constructors**
root@ajay-PC:/home/ajay/c++# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root@ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Destructor
Destructor
Destructor
Destructor

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 (ie elide) observable side-effects. 复制省略是允许具有(即消除)可观察到的副作用的唯一优化形式。 Example: 例:

#include <iostream>
int n = 0;
class ABC
{  public:  ABC(int) {}    ABC(const ABC& a) { ++n; } // the copy constructor has a visible side effect
};                     // it modifies an object with static storage duration    int main()
{  ABC c1(21); // direct-initialization, calls C::C(42)  ABC c2 = ABC(21); // copy-initialization, calls C::C( C(42) )  std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwisereturn 0;
}Output without -fno-elide-constructors
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp
root@ajay-PC:/home/ayadav# ./a.out
0Output with -fno-elide-constructors
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root@ajay-PC:/home/ayadav# ./a.out
1

GCC provides the -fno-elide-constructors option to disable copy elision. GCC提供了-fno-elide-constructors elide -fno-elide-constructors选项来禁用复制省略。 If you want to avoid possible copy elision, use -fno-elide-constructors . 如果要避免可能的复制省略,请使用-fno-elide-constructors elide -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. 每次删除副本时,都省略了副本的一种构造和一种匹配破坏,从而节省了CPU时间,并且不创建一个对象,从而节省了堆栈框架上的空间。

什么是复制省略和返回值优化?相关推荐

  1. 浅谈C++11标准中的复制省略(copy elision,也叫RVO返回值优化)

    严正声明:本文系作者davidhopper原创,未经许可,不得转载. C++11以及之后的C++14.17标准均提出一项编译优化技术:复制省略(copy elision,也称复制消除),另外还有RVO ...

  2. C++编程法则365条一天一条(358)copy elision(返回值优化NVO和具名返回值优化NRVO)

    文章目录 强制编译器实现的优化 非强制实现优化 参考:https://en.cppreference.com/w/cpp/language/copy_elision Elision 是省略.删节或者忽 ...

  3. C++的返回值优化(RVO,Return Value Optimization)

    前言 大家都知道"过早的优化是万恶之源"这句话,然而我相信其中的大多数人都不知道自己是不是在做过早的优化.我也无法准确的定义什么叫做"过早的优化",但我相信这& ...

  4. c++中返回值优化(RVO)和命名返回值优化(NRVO)介绍

    RVO和NRVO介绍 前言 半年前就想写一篇关于RVO和NRVO的介绍,但碍于没什么时间去写博客.在跟身边人进行学术探讨的时候,会发现部分人可能尝到了编译器给它做返回值优化的好处,知道这段代码被优化了 ...

  5. 提高C++性能的编程技术笔记:虚函数、返回值优化+测试代码

    虚函数:在以下几个方面,虚函数可能会造成性能损失:构造函数必须初始化vptr(虚函数表):虚函数是通过指针间接调用的,所以必须先得到指向虚函数表的指针,然后再获得正确的函数偏移量:内联是在编译时决定的 ...

  6. C++高阶 返回值优化--RVO和NRVO介绍

    RVO即返回值优化(return value optimize),可以少做一次拷贝构造. NRVO是具名返回值的意思,起初RVO技术仅支持匿名变量的优化,后期才加入对具名变量的优化. RVO: Big ...

  7. C++之RVO返回值优化

    什么是RVO优化 RVO的全称是Return Value Optimization.RVO是一种编译器优化技术,可以把通过函数返回创建的临时对象给"去掉",然后可以达到少调用拷贝构 ...

  8. C++ 返回值优化 RVO

    C++ 返回值优化 RVO 引子 返回值优化 RVO RVO 限制 参考 最近在调试代码时,发现拷贝和移动系列构造函数的调用和预期不太一样,经过查阅相关资料,发现是返回值优化(RVO)从中做梗.了解了 ...

  9. 探索 C++ 返回值优化(ROV)

    以下为示例代码 1.以下为不开启ROV的前提的汇编(-fno-elide-constructors) <main函数汇编> <calculate2函数汇编> 这时候会执行一次普 ...

最新文章

  1. sklearn GaussianNB(高斯朴素贝叶斯)模型使用RandomSearchCV获取最优参数及可视化​​​​​​​
  2. Python书单,不将就
  3. Powershell 命令行泄漏下一个 Windows 10 更新内容
  4. python程序设计报告-20183122 实验一《Python程序设计》实验报告
  5. 模拟一个连接来自搜索引擎
  6. 第23天学习Java的笔记-抽象类
  7. linux fastQC 操作命令,linux-ubuntu下fastQC的安裝及使用
  8. 删除Windows Service
  9. window连接不上ssdb的问题
  10. linux文件系统挂载不上,nfs文件系统在linux下挂载不上的解决办法
  11. 物联网形势大好,传感器前景可观
  12. android usb没有读写节点,2019踩坑无数含泪写下最新教程系列(三)树莓派挂载android(树莓派通过usb读取手机里面档案)...
  13. C# Winform编程ListBox之DrawItem事件
  14. 20200802每日一句(0729--0802)
  15. ios开发人员行为准则_如何成为iOS开发人员
  16. Ubuntu 19.04 磁盘加密
  17. word删除空白页删不了怎么办?Word怎么删除空白页?
  18. 用命令从FTP服务器下载文件
  19. 文字加下划线单选按钮效果,RadioGroup实现
  20. 24点游戏开发实例(Qt含源码)

热门文章

  1. Android开发之Dialog的三种列表显示(解读谷歌官方API)
  2. ListView 常用属性
  3. 【Android View事件分发机制】滑动冲突
  4. Oracle数据库基础入门《二》Oracle内存结构
  5. 【Cucumber】【命令行】
  6. VS2010/MFC编程入门之十七(对话框:文件对话框)
  7. vue-router 的重定向-redirect
  8. 在linux下给grep命令添加颜色
  9. JavaScript [ 转 ] —— 面向对象编程(三):非构造函数的继承
  10. js一些稀奇古怪的写法-带你装逼带你飞