I'm trying to understand when to create a custom swap() function for my classes. Researching it previously, it looks like ever since c++11 the std::swap() function has used move syntax and that if you want to call swap on your class, it's often best to just use the std version since move symantics are often the most effective solution to swapping even classes with deep resources. I've seen it recommended to only use custom swap functions if you have a bizarre class that won't work with the std version, such as a class that isn't movable but you do want to be swappable.
In that case, why do the c++ core guidelines recommend defining your own anytime you have a non trivially copyable class? If anything I figure it'd be more effective to remember to define one anytime you don't have a move constructor (but still want to be able to swap it).
I'm referencing discussions like this, which come to the conclusion that creating a custom swap now feels more like a special case, thanks to move semantics. Std::swap() no longer uses copy semantics, which makes it feel odd that in C.83 the recommendation for whether or not to make one is based off of whether or not it's trivially copyable, not movable.
The guideline says to "consider" providing a swap, which is a bit weaker than recommending one.
Explicitly providing a noexcept
member swap helps document whether a swap will indeed be noexcept
.
The enforcement section says a non-trivially copyable class "should" have a swap. If it's not trivially copyable, then you had to provide a copy constructor and copy assignment operator, which will suppress the automatic generation of the move constructor and move assignment operator. If you forgot to add those explicitly, then std::swap
will quietly fall back to three copies and won't (necessarily) be noexcept
.
As the guideline notes, a common pattern is to implement copy assignment in terms of swap and a copy constructor. Having an explicit swap ensures this pattern works as intended.
If the object is large because it has many members, then the std::swap
is going to put a large temporary on the stack. Your own swap method will only need enough stack to temporarily store the largest single data member.
If one of the object's data members failed to provide a way to do a noexcept
swap (either by explicitly providing one or by ensuring it has noexcept
move semantics), then you'll increase your chances of finding that sooner rather than later.