Daniel Langr
Daniel Langr

Reputation: 23497

When to provide custom swap function in C++?

Consider a custom class X holding some resource (for simplicity represented here by an integer member variable) with move constructor (MC), move assignment operator (MAO), and a custom swap function:

class X {
  int i_;
public:
  X(int i) : i_(i) { }
  X(X&& rhs) = default; // move constructor 
  X& operator=(X&& rhs) = default; // move assignment operator 
  // X& operator=(X rhs) { std::swap(i_, rhs.i_); return *this; } // unifying assign. op.
  friend void swap(X& lhs, X& rhs) { std::swap(lhs.i_, rhs.i_); }
  int i() const { return i_; }
};

In the following benchmark code:

int main() {
  int i1, i2;
  std::cin >> i1 >> i2;

  X x1(i1), x2(i2);
  std::cout << x1.i() << x2.i();

  swap(x1, x2);
  std::cout << x1.i() << x2.i();

  std::swap(x1, x2);
  std::cout << x1.i() << x2.i();
}
  1. swap(x1, x2) simply swaps 2 integers.
  2. std::swap(x1, x2) calls 1x MC and 2x MAO.
  3. If MAO is substituted by unifying assignment operator (UAO), std::swap(x1, x2) calls 3x MC and 2x UAO.

(Tested with gcc/libstdc++).

Seemingly, custom swap is very valuable since it avoids many function calls. However, when I look at dissassemled output of this program: https://godbolt.org/g/RMfZgL, all the functionality of MC, MAO, UAO, swap and std::swap is inlined directly into main (gcc/clang with -O2 flag). Moreover, no object of type X is created in the program, it works only with integers at the machine code level.

Therefore, it seems to me that there is no reason to provide custom swap function for a class like X.

QUESTIONS:

  1. Is it good practice to assume that a compiler will optimize out calls of MC and MAO/UAO inside std::swap? Or, should I rather define custom swap for all custom classes?
  2. For what kinds of classes is custom swap really beneficial?

Upvotes: 4

Views: 1114

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275565

You should follow the rule of 0.

Non-resource managing classes should have default move/copy/assign/destruction.

Resource managing classes should be only concerned with managing resources.

Your code does not manage resources. Everything copy/move wise should be defaulted.

swap is a special case of move. In the case you are a resource managing type, you may want to also write it. However, like most things, it is an optimization, and you shoud only do such optimizations if you have proven it has performance impact.

An example of when it might have performance impact is if you have a never-empty resource-owning type and that resource isn't free, so creating a temporary object has unavoidable costs. std::swap by default moves through a throw-away object. A custom swap could avoid this.

Or, a type that is a large buffer of cheap to move elements; creating that large buffer could have real costs.

Otherwise, don't do it. And test if you do it that it helps. Custom swap is a optimization of move for a particular case.

Upvotes: 7

Related Questions