复制省略是一种编译器优化技术,可消除不必要的对象复制/移动。
在以下情况下,允许编译器省略复制/移动操作,因此不调用相关的构造函数:
-
NRVO(命名返回值优化):如果函数按值返回类类型,并且return语句的表达式是具有自动存储持续时间的非易失性对象的名称(不是函数参数),则可以省略非优化编译器执行的复制/移动。如果是这样,则返回值直接在存储中构造,否则函数的返回值将被移动或复制到该存储中。
-
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()); //RVO, not 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
GCC 提供了-fno-elide-constructors 选项来禁用复制省略。
如果您想避免可能的复制省略,请使用-fno-elide-constructors。
现在几乎所有编译器都在启用优化时提供复制省略(如果没有设置其他选项来禁用它)。
结论
每次复制省略,副本的一个构造和一个匹配的销毁被省略,从而节省CPU时间,并且不创建一个对象,从而节省堆栈帧上的空间。