什么是复制省略,它如何优化复制交换惯用语?

34

我正在阅读“拷贝并交换”

我试着阅读一些关于“拷贝省略(Copy Elision)”的链接,但是没有完全明白它的含义。有人可以解释一下这个优化是什么意思吗,特别是以下文字是什么意思:

这不仅是方便的问题,实际上是一种优化。如果参数(s)绑定到一个左值(另一个非 const 对象),则在创建参数(s)时会自动复制对象。然而,当 s 绑定到右值(临时对象,字面量)时,通常会省略复制操作,从而避免了调用复制构造函数和析构函数。在早期版本的赋值运算符中,当引用绑定到右值时,并不会发生拷贝省略。这会导致多创建和销毁一个额外的对象。


2个回答

38

拷贝构造函数的存在是为了进行复制。理论上,当你写下这样一行代码:

CLASS c(foo());
编译器将需要调用复制构造函数来将foo()的返回值复制到c中。
复制省略是一种跳过调用复制构造函数的技术,以避免支付开销。
例如,编译器可以安排foo()直接构造其返回值并将其放入c中。
这里有另一个例子。假设您有一个函数:
void doit(CLASS c);

如果您使用实参调用它,编译器必须调用复制构造函数,以防止修改原始参数:

CLASS c1;
doit(c1);

但是现在考虑一个不同的例子,假设你像这样调用了你的函数:

doit(c1 + c1);

operator+将不得不创建一个临时对象(一个rvalue)。编译器可以在调用doit()之前传递由operator+创建的临时对象,并将其传递给doit(),而不是在调用doit()之前调用复制构造函数。


3
这是一个例子:
#include <vector>
#include <climits>

class BigCounter {
 public:
   BigCounter &operator =(BigCounter b) {
      swap(b);
      return *this;
   }

   BigCounter next() const;

   void swap(BigCounter &b) {
      vals_.swap(b);
   }

 private:
   typedef ::std::vector<unsigned int> valvec_t;
   valvec_t vals_;
};

BigCounter BigCounter::next() const
{
   BigCounter newcounter(*this);
   unsigned int carry = 1;
   for (valvec_t::iterator i = newcounter.vals_.begin();
        carry > 0 && i != newcounter.vals_.end();
        ++i)
   {
      if (*i <= (UINT_MAX - carry)) {
         *i += carry;
      } else {
         *i += carry;
         carry = 1;
      }
   }
   if (carry > 0) {
      newcounter.vals_.push_back(carry);
   }
   return newcounter;
}

void someFunction()
{
    BigCounter loopcount;
    while (true) {
       loopcount = loopcount.next();
    }
}

在一些函数中,loopcount = loopcount.next(); 这行代码可以极大地受益于复制省略。如果不允许复制省略,这一行代码将需要调用三次复制构造函数,并伴随着一次析构函数的调用。而有了复制省略的允许,它可以被减少为一次复制构造函数的调用,在 BigCount::next() 中显式声明 newcounter 的那一次调用。
如果 operator = 被声明和定义成这样:
BigCounter &BigCounter::operator =(const BigCounter &b) {
   BigCounter tmp(b);
   swap(tmp);
   return *this;
}

即使使用复制省略,也必须调用2次复制构造函数才能构造`newcounter`和`tmp`。而如果没有复制省略,那么就需要3次。这就是声明赋值运算符的参数为复制构造函数类型可以作为“复制并交换”范式中的一项优化。当调用复制构造函数用于构造参数时,其调用可以被省略,但如果用于创建本地变量,则不可省略。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接