Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is copy-and-swap always the best solution?

Tags:

c++

I have seen the copy-and-swap idiom recommended in various places as the recommended/best/only way to implement strong exception safety for the assignment operator. It seems to me that this approach also has a downside.

Consider the following simplified vector-like class which utilizes copy-and-swap:

class IntVec {
  size_t size;
  int* vec;
public:
  IntVec()
    : size(0),
      vec(0)
  {}
  IntVec(IntVec const& other)
    : size(other.size),
      vec(size? new int[size] : 0)
  {
    std::copy(other.vec, other.vec + size, vec);
  }

  void swap(IntVec& other) {
    using std::swap;
    swap(size, other.size);
    swap(vec, other.vec);
  }

  IntVec& operator=(IntVec that) {
    swap(that);
    return *this;
  }

  //~IntVec() and other functions ...
}

Implementing the assignment via the copy constructor may be efficient and guarantee exception safety, but it can also cause an unneeded allocation, potentially even causing an uncalled for out-of-memory error.

Consider the case of assigning a 700MB IntVec to a 1GB IntVec on a machine with a <2GB heap limit. An optimal assignment will realize it already has enough memory allocated and only copy the data into it's already allocated buffer. The copy-and-swap implementation will cause an allocation of another 700MB buffer before the 1GB one is released, causing all 3 to try co-exist in memory at once, which will throw an out-of-memory error needlessly.

This implementation would solve the problem:

IntVec& operator=(IntVec const& that) {
  if(that.size <= size) {
    size = that.size;
    std::copy(that.vec, that.vec + that.size, vec);
  } else
    swap(IntVec(that));
  return *this;
}

So the bottom line is:
Am I right that this is a problem with the copy-and-swap idiom, or do normal compiler optimizations somehow eliminate the extra allocation, or am I overlooking some problem with my "better" version that the copy-and-swap one solves, or am I doing my math/algorithms wrong and the problem doesn't really exist?

like image 891
Baruch Avatar asked May 28 '14 17:05

Baruch


People also ask

What is the copy and swap idiom?

Copy-and-Swap Idiom in C++ But when overloading the assignment operator, it can become quite difficult to implement. The copy and swap idiom is a solution for the same. This idiom uses the copy-constructor to build a local copy of the data. It then swaps the old data with the new data using the swap function.

How does STD swap work?

The std::swap() function is a built-in function in the C++ STL. The swap(T& a, T& b) function calls by reference and the C++ overloads swap( ) function based on the data types of the variables passes, if the variables pass of different data types the swap( ) function throw error or exception.


1 Answers

There are two problems with the implementation reusing the space

  • If you're assigning very_huge_vect = very_small_vect; the extra memory will not be released. This may be what you want or may be not.

  • In case of integers all is fine, but what about objects for which the copy operation may throw an exception? You'll end up with a messed up array where part of the copy has been done and that has been truncated. Much better would be to leave the target untouched if the copy operation fails (what the swap idiom does).

By the way, as a general rule, in very few cases you can find anything that looks like "always the best solution". If you're looking for a silver bullet, programming is not going to be the right place.

like image 178
6502 Avatar answered Oct 08 '22 17:10

6502