编译器拷贝优化(copy elision)

Copy Elision是大多数编译器都会执行的一项优化。gcc的手册里面是这样描述的:

-fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary which is only used to initialize another object of the same type. Specifying this option disables that optimization, and forces G++ to call the copy constructor in all cases.

当需要创建一个只是用来初始化其他对象的临时对象时,编译器可以忽略此操作,不执行拷贝构造函数(copy-initialization),而是直接把临时对象创建在目标对象的空间上(direct-initialization),从而提高程序的性能。例如:
[ccb_c++]
class foo
{
public:
foo() {cout<<"ctor"<如何利用拷贝优化

假设我们需要写这样一个函数:
[ccb_c++]
int f1(const foo &a)
{
foo tmp(a);
do_something(tmp);

return 0;
}
[/ccb_c++]
为了避免改变参数,函数f1生成一份参数的拷贝,更改复制得到的对象。其实这样做并没有什么好处,既然复制操作无法避免,那就让它来的自然点好了。
[ccb_c++]
int f2(foo a)
{
do_something(a);

return 0;
}
[/ccb_c++]
与f1相比,f2的好处在于,当参数是右值的时候,拷贝操作被省略了。如果参数是左值,那么两个函数都会进行一次拷贝。

但有一点需要注意,如果函数将参数返回,那么RVO将不会起作用。
[ccb_c++]
foo f3(foo a)
{
do_something(a);

return a;
}
[/ccb_c++]
其实这也很容易理解,编译器在编译使用此函数的代码时,必定会使用两个地址,一个存放按值传递的参数,另一个存放返回值。在返回的时候,一定执行一次拷贝构造函数,将参数复制到返回值的地址上。更详细的解释参见Why is RVO disallowed when returning a parameter?

即使不能利用RVO,我们也可以将函数优化。一般而言,默认构造函数和swap操作会比拷贝构造函数更快,所以可以这样写:
[ccb_c++]
foo f4(foo a)
{
do_something(a);
foo tmp;
tmp.swap(a);

return tmp;
}
[/ccb_c++]

参考

  1. The Name Return Value Optimization
  2. C++: Exploiting Copy Elisions
  3. Why is RVO disallowed when returning a parameter?
  4. Is RVO (Return Value Optimization) guaranteed for all objects in gcc compilers?

《编译器拷贝优化(copy elision)》有1个想法

  1. 最近在了解-fno-elide-constructors有什么用,无意中搜到了博主的文章,写得很清楚,很明白,看完之后获益良多,感谢博主的文章。

发表评论

电子邮件地址不会被公开。 必填项已用*标注