Copy&Swap efficiency for shared pointers

226 Views Asked by At

This can be seen as a follow up to e.g. Why shared pointer assignment does 'swap'?.

The question is about the Copy&Swap idiom used e.g. in boost.

I do understand that the benefit of Copy&Swap is to reuse existing code which avoids duplication and bugs. But there are 2 cases (actually 1 can be reduced to the other) where it is not optimal:

  1. The smart pointer instances are the same
  2. The contained pointer is the same

For shared_ptr the ref counters are incremented atomically and for intrusive_ptr(boost only) they may be. So there is a high cost for a copy.

This can be avoided, if the assignment was implemented like:

smart_ptr& operator=(const smart_ptr& other){
  if(this->ptr_ == other.ptr_) return *this;
  smart_ptr(other).swap(*this); // I assume I can simply do this here, right?
  return *this;
}
smart_ptr& operator=(smart_ptr&& other){
  smart_ptr(std::move(other)).swap(*this);
  return *this;
}

Wouldn't this be the fastest and safest implementation or is there any issue I did not see?

If it is the fastest, why aren't boost or the stdlib using it?

To clarify on point 2. consider the following code:

smart_ptr a(new foo);
auto b = a;
...
// Eventually:
a = b;

This is not self-assignment as &a != &b. The Copy&Swap does involve an unnecessary modification of the reference counter.

1

There are 1 best solutions below

4
On

The self assignment is not a common case so it will cost more to test that each time then to swap it either way. Also copying a pointer is basically the fastest copy one can perform.

The real cost of copying shared_ptr is atomic reference increment (which may involve using mutex underneath).

If you really want to test performance of both approach I would recommend getting google benchmark library and write a set of test cases (for self assigment and all other cases) and to measure it. Remember that optimizer these days can do marvels with your code to optimize it. Without measuring it, it would be hard to tell if this is faster, but I guess as if are pretty expensive the version without it is better :)

EDIT:

If you don't want to reference count to increase (which is expensive part with copying shared_ptr) you can always use move constructor:

smart_ptr a(new foo);
auto b = a;
...
// Eventually:
a = std::move(b);