### Copy elision & RVO

##### 由于C++标准允许编译器对代码进行任何优化，所以了解这两者如何产生以及如何防止可以让我们更好的掌控所写的代码

Optimizes out copy- and move- constructor, resulting in zero-copy pass-by-value semantics.

• 在初始化中，如果初始化表达式是一个prvaluecv-unqualified且他们的源类型和目的类型相同：
  T x = T(T(T())); // 只会有一个调用T的默认构造函数来初始化x

• 在函数调用中，如果return语句的操作数是prvalue并且这个prvalue和函数的返回类型相同。
 T f() { return T(); }
T x = f(); // 只会有一个调用T的默认构造函数来初始化x
T* p = new T(f()) // 只会有一个调用T的默认构造函数来初始化*p


• 如果一个函数以值的形式返回一个类且满足如下所有情况，那么复制和移动构造将被忽略，这时，当这个局部对象被构造时，它被直接构造成函数的返回值；否则就使用复制或移动构造。这个copy elision的变种被称为NRVO，“named return value optimization”。
• 这个类不是带有自动储存时间的volatile对象
• 不是函数参数或者catch语句的参数
• 和返回类型相同（忽略顶层cv标识)
• 当一个未绑定到任何引用的无名临时对象可以移动或复制到一个有相同类型（忽略顶层cv标识）的对象Object时，那么复制和移动构造将被忽略，这时，当这个临时对象被构造时，它被直接构造成Object；否则使用复制或移动构造。当这个临时对象是return语句的参数时，这个copy elision的变种被称为RVO,"return value optimization"。

C++11起，又多了两种情况

• In a throw-expression, if the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and whose scope does not extend past the innermost try-block (if there is a try-block), then copy/move is omitted. When that local object is constructed, it is constructed directly in the storage where the exception object would otherwise be moved or copied to.

• When handling an exception, if the argument of the catch clause is of the same type (ignoring top-level cv-qualification) as the exception object thrown, the copy is omitted and the body of the catch clause accesses the exception object directly, as if caught by reference. This is disabled if such copy elision would change the observable behavior of the program for any reason other than skipping the copy constructor and the destructor of the catch clause's parameter.

C++14起，又多了种情况

• In constant expression and constant initialization, all copy elision is guaranteed (note: this is by post-C++14 defect report CWG 2022).
  struct A {
void *p;
constexpr A(): p(this) {}
};

constexpr A g() {
A a;
return a;
}

constexpr A a;        // a.p points to a
constexpr A b = g();  // b.p points to b (NRVO guaranteed)

void g() {
A c = g();          // c.p may point to c or to an ephemeral temporary
}

##### 接下来看如下代码：
#include <iostream>

using std::cout;

struct Obj
{
Obj() { cout << "constructor.\n"; }
Obj(const Obj&) { cout << "copy constructor.\n"; }
};

int main()
{
Obj obj = Obj();
}


./a.out
constructor.
copy constructor.


./a.out
constructor.


##### 接下来看两种RVO
Obj foo()
{
return Obj(); // RVO
}

Obj bar()
{
Obj res;
return res; // NRVO
}

int main()
{
Obj obj(foo());
cout << "-----------------\n";
Obj obj1(bar());
}


##### 注意

Copy elision is the only allowed form of optimization that can change the observable side-effects. Because some compilers do not perform copy elision in every situation where it is allowed (e.g., in debug mode), programs that rely on the side-effects of copy/move constructors and destructors are not portable.

Obj baz(int i)
{
Obj res;
if(i > 0)
return res;
else
return Obj();
}


In a return statement or a throw-expression, if the compiler cannot perform copy elision but the conditions for copy elision are met or would be met, except that the source is a function parameter, the compiler will attempt to use the move constructor even if the object is designated by an lvalue; see return statement for details. -- since C++11

struct Obj
{
static int count;
Obj(){}
Obj(const Obj&) { count += 1; }
};
int Obj::count = 0;
int main()
{
Obj obj = Obj();
Obj obj1 = Obj();
cout << Obj::count << endl;
}