What is copy elision and how does it optimize the copy

I was reading Copy and Swap.

I tried reading some links on Copy Elision but could not figure out properly what it meant. Can somebody please explain what this optimization is, and especially what is mean by the following text

This is not just a matter of convenience but in fact an optimization. If the parameter (s) binds to a lvalue (another non-const object), a copy of the object is made automatically while creating the parameter (s). However, when s binds to a rvalue (temporary object, literal), the copy is typically elided, which saves a call to a copy constructor and a destructor. In the earlier version of the assignment operator where the parameter is accepted as const reference, copy elision does not happen when the reference binds to a rvalue. This results into an additional object being created and destroyed.


The copy constructor exists to make copies. In theory when you write a line like:

CLASS c(foo());

The compiler would have to call the copy constructor to copy the return of foo() into c .

Copy elision is a technique to skip calling the copy constructor so as not to pay for the overhead.

For example, the compiler can arrange that foo() will directly construct its return value into c .

Here's another example. Let's say you have a function:

void doit(CLASS c);

If you call it with an actual argument, the compiler has to invoke the copy constructor so that the original parameter cannot be modified:

CLASS c1;
doit(c1);

But now consider a different example, let's say you call your function like this:

doit(c1 + c1);

operator+ is going to have to create a temporary object (an rvalue). Instead of invoking the copy constructor before calling doit() , the compiler can pass the temporary that was created by operator+ and pass that to doit() instead.


Here is an example:

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

In somefunction the line loopcount = loopcount.next(); benefits greatly from copy elision. If copy elision were not allowed, that line would require 3 invocations of the copy constructor and an associated call to a destructor. With copy elision being allowed, it can be reduced to 1 call of the copy constructor, the explicit one inside of BigCount::next() where newcounter is declared.

If operator = had been declared and defined like this:

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

there would've had to have been 2 invocations of the copy constructor, even with copy elision. One to construct newcounter and the other to construct tmp . And without copy elision there would still be 3. That's why declaring operator = so its argument requires invoking the copy construct can be an optimization when using the 'copy and swap' idiom for the assignment operator. When the copy constructor is invoked for constructing an argument, its invocation may be elided, but if it's invoked to create a local variable, it may not be.

链接地址: http://www.djcxy.com/p/12672.html

上一篇: 动态分配一个对象数组

下一篇: 什么是复制省略以及它如何优化副本