32

我正在阅读Copy and Swap

我尝试阅读有关 Copy Elision 的一些链接,但无法正确理解它的含义。有人可以解释一下这个优化是什么,特别是下面的文字是什么意思

这不仅仅是方便的问题,实际上也是一种优化。如果参数(s)绑定到左值(另一个非常量对象),则在创建参数时会自动制作对象的副本。但是,当 s 绑定到右值(临时对象,文字)时,副本通常会被省略,这样可以节省对复制构造函数和析构函数的调用。在参数被接受为 const 引用的赋值运算符的早期版本中,当引用绑定到右值时,不会发生复制省略。这会导致创建和销毁一个额外的对象。

4

2 回答 2

35

复制构造函数的存在是为了制作副本。理论上,当您编写如下行时:

CLASS c(foo());

编译器必须调用复制构造函数来复制foo()into的返回c

复制省略是一种跳过调用复制构造函数以便不支付开销的技术。

例如,编译器可以安排foo()将其直接构造成c.

这是另一个例子。假设您有一个功能:

void doit(CLASS c);

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

CLASS c1;
doit(c1);

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

doit(c1 + c1);

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

于 2010-01-27T00:52:30.510 回答
2

这是一个例子:

#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();
    }
}

somefunction在行中loopcount = loopcount.next();从复制省略中受益匪浅。如果不允许复制省略,则该行将需要复制构造函数的 3 次调用以及对析构函数的关联调用。在允许复制省略的情况下,它可以减少到复制构造函数的 1 次调用,在BigCount::next()wherenewcounter中显式地声明了一次。

如果operator =已像这样声明和定义:

BigCounter &BigCounter::operator =(const BigCounter &b) {
   BigCounter tmp(b);
   swap(tmp);
   return *this;
}

即使使用复制省略,也必须调用 2 次复制构造函数。一个构建newcounter,另一个构建tmp。如果没有复制省略,仍然会是 3。这就是为什么声明operator =它的参数需要调用复制构造在使用赋值运算符的“复制和交换”习惯用法时可以是一种优化。当复制构造函数被调用来构造一个参数时,它的调用可能会被省略,但如果它被调用来创建一个局部变量,它可能不会。

于 2010-01-28T08:12:13.763 回答