2

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?
Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • Use good judgment to build a hypothesis and test it. There is really no way to predict what optimizer will do. With C++17 they will be mandated to use copy elision in certain cases, but that's about it as it comes to mandatory optimizations. – SergeyA Aug 11 '17 at 16:48
  • 1
    Some good points are made here: https://stackoverflow.com/a/2078987/4342498 – NathanOliver Aug 11 '17 at 17:15
  • @NathanOliver Great answer. It really looks like we do not need `swap` anymore for classes that support move semantics. For copyable but non-moveable classes, custom `swap` makes sense. – Daniel Langr Aug 11 '17 at 17:25
  • @DanielLangr That is my feelings. If t is movable there really isn't a reason to need a custom swap. If not then it can still makes sense. – NathanOliver Aug 11 '17 at 17:31
  • @NathanOliver I just found a case where, in my opinion, custom `swap` function might be more efficient than `std::swap` even for movable types, see https://stackoverflow.com/a/45689282/580083. Could you, please, check that out? – Daniel Langr Aug 15 '17 at 08:41

1 Answers1

4

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524