Reputation: 23497
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();
}
swap(x1, x2)
simply swaps 2 integers. std::swap(x1, x2)
calls 1x MC and 2x MAO.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:
std::swap
? Or, should I rather define custom swap
for all custom classes?swap
really beneficial?Upvotes: 4
Views: 1114
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