C++ 什么是复制省略和返回值优化?

C++ 什么是复制省略和返回值优化?,c++,optimization,c++-faq,return-value-optimization,copy-elision,C++,Optimization,C++ Faq,Return Value Optimization,Copy Elision,什么是复制省略?什么是(命名)返回值优化?它们意味着什么 在什么情况下会发生这种情况?限制是什么 如果你提到这个问题,你可能在寻找 有关技术概述,请参阅 看 导言 有关技术概述- 对于发生复制省略的常见情况- 拷贝省略是大多数编译器实现的一种优化,用于在某些情况下防止额外的(可能昂贵的)拷贝。它使按值返回或按值传递在实践中可行(适用限制) 这是唯一一种省略的优化形式(哈!)即使复制/移动对象有副作用,也可以应用“仿佛”规则-复制省略 以下示例取自: struct C{ C(){} C(cons

什么是复制省略?什么是(命名)返回值优化?它们意味着什么

在什么情况下会发生这种情况?限制是什么

  • 如果你提到这个问题,你可能在寻找
  • 有关技术概述,请参阅
导言 有关技术概述-

对于发生复制省略的常见情况-

拷贝省略是大多数编译器实现的一种优化,用于在某些情况下防止额外的(可能昂贵的)拷贝。它使按值返回或按值传递在实践中可行(适用限制)

这是唯一一种省略的优化形式(哈!)即使复制/移动对象有副作用,也可以应用“仿佛”规则-复制省略

以下示例取自:

struct C{
C(){}
C(const C&{std::cout简介
有关技术概述-

对于发生复制省略的常见情况-

拷贝省略是大多数编译器实现的一种优化,用于在某些情况下防止额外的(可能昂贵的)拷贝。它使按值返回或按值传递在实践中可行(适用限制)

这是唯一一种省略的优化形式(哈!)即使复制/移动对象有副作用,也可以应用“仿佛”规则-复制省略

以下示例取自:

struct C{
C(){}
C(常数C&){std::cout标准参考
对于技术性较低的视图和介绍-

对于发生复制省略的常见情况-

复制省略在标准中定义为:

12.8复制和移动类对象[class.copy] 作为

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

-在具有类返回类型的函数中的return语句中,当表达式是 具有相同参数的非易失性自动对象(函数或catch子句参数除外) 类型作为函数返回类型,可以通过构造 自动对象直接输入函数的返回值

-在抛出表达式中,当操作数是非易失性自动对象(而不是 函数或catch子句参数),其范围不超出最内层 封闭try块(如果有),从操作数到异常的复制/移动操作 通过将自动对象直接构造到异常对象中,可以省略对象(15.1)

-复制/移动未绑定到引用(12.2)的临时类对象时 对于具有相同cv类型的类对象,可以通过 将临时对象直接构造到省略的复制/移动的目标中

-当异常处理程序(第15条)的异常声明声明相同类型的对象时 (cv鉴定除外)作为例外对象(15.1),可以省略复制/移动操作 通过将异常声明视为异常对象的别名,如果程序的含义 将保持不变,除了为声明的对象执行构造函数和析构函数 异常声明

123)因为只有一个对象被销毁,而不是两个,并且没有执行一个复制/移动构造函数,所以仍然有一个 为每个构造的对象销毁

举例如下:

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

在这里,省略的条件可以组合起来,以消除对类
Thing
的复制构造函数的两个调用: 将本地自动对象
t
复制到函数
f()返回值的临时对象中
将临时对象复制到对象
t2
。有效地,本地对象
t
可以视为直接初始化全局对象
t2
,并且该对象的销毁将在程序中发生 将move构造函数添加到Thing具有相同的效果,但它是来自 被省略的
t2
的临时对象

标准参考 对于技术性较低的视图和介绍-

对于发生复制省略的常见情况-

复制省略在标准中定义为:

12.8复制和移动类对象[class.copy] 作为

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

-在具有类返回类型的函数的return语句中,当
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
}
class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  Thing t;
  return t;
}
Thing t2 = f();
class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  Thing t;
  return t;
}
Thing t2 = f();
class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  return Thing();
}
Thing t2 = f();
class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
void foo(Thing t);

foo(Thing());
struct Thing{
  Thing();
  Thing(const Thing&);
};
 
void foo() {
  Thing c;
  throw c;
}
 
int main() {
  try {
    foo();
  }
  catch(Thing c) {  
  }             
}
#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  
#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 otherwise
  return 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   
0

Output 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
# include <iostream>


class Obj {
public:
  int var1;
  Obj(){
    std::cout<<"In   Obj()"<<"\n";
    var1 =2;
  };
  Obj(const Obj & org){
    std::cout<<"In   Obj(const Obj & org)"<<"\n";
    var1=org.var1+1;
  };
};

int  main(){

  {
    /*const*/ Obj Obj_instance1;  //const doesn't change anything
    Obj Obj_instance2;
    std::cout<<"assignment:"<<"\n";
    Obj_instance2=Obj(Obj(Obj(Obj(Obj_instance1))))   ;
    // in fact expected: 6, but got 3, because of 'copy elision'
    std::cout<<"Obj_instance2.var1:"<<Obj_instance2.var1<<"\n";
  }

}

In   Obj()
In   Obj()
assignment:
In   Obj(const Obj & org)
Obj_instance2.var1:3