C++ compilers automatically generate copy constructors and copy-assignment operators. Why not swap too?
These days the preferred method for implementing the copy-assignment operator is the copy-and-swap idiom:
T& operator=(const T& other)
{
T copy(other);
swap(copy);
return *this;
}
(ignoring the copy-elision-friendly form that uses pass-by-value).
This idiom has the advantage of being transactional in the face of exceptions (assuming that the swap implementation does not throw). In contrast, the default compiler-generated copy-assignment operator recursively does copy-assignment on all base classes and data members, and that doesn’t have the same exception-safety guarantees.
Meanwhile, implementing swap methods manually is tedious and error-prone:
- To ensure that
swapdoes not throw, it must be implemented for all non-POD members in the class and in base classes, in their non-POD members, etc. - If a maintainer adds a new data member to a class, the maintainer must remember to modify that class’s
swapmethod. Failing to do so can introduce subtle bugs. Also, sinceswapis an ordinary method, compilers (at least none I know of) don’t emit warnings if theswapimplementation is incomplete.
Wouldn’t it be better if the compiler generated swap methods automatically? Then the implicit copy-assignment implementation could leverage it.
The obvious answer probably is: the copy-and-swap idiom didn’t exist when C++ was developed, and doing this now might break existing code.
Still, maybe people could opt-in to letting the compiler generate swap using the same syntax that C++0x uses for controlling other implicit functions:
void swap() = default;
and then there could be rules:
- If there is a compiler-generated
swapmethod, an implicit copy-assignment operator can be implemented using copy-and-swap. - If there is no compiler-generated
swapmethod, an implicit copy-assignment operator would be implemented as before (invoking copy-assigment on all base classes and on all members).
Does anyone know if such (crazy?) things have been suggested to the C++ standards committee, and if so, what opinions committee members had?
This is in addition to Terry’s answer.
The reason we had to make
swapfunctions in C++ prior to 0x is because the general free-functionstd::swapwas less efficient (and less versatile) than it could be. It made a copy of a parameter, then had two re-assignments, then released the essentially wasted copy. Making a copy of a heavy-weight class is a waste of time, when we as programmers know all we really need to do is swap the internal pointers and whatnot.However, rvalue-references relieve this completely. In C++0x,
swapis implemented as:This makes much more sense. Instead of copying data around, we are merely moving data around. This even allows non-copyable types, like streams, to be swapped. The draft of the C++0x standard states that in order for types to be swapped with
std::swap, they must be rvalue constructable, and rvalue assignable (obviously).This version of
swapwill essentially do what any custom written swap function would do. Consider a class we’d normally writeswapfor (such as this “dumb” vector):Previously,
swapwould make a redundant copy of all our data, before discarding it later. Our customswapfunction would just swap the pointer, but can be clumsy to use in some cases. In C++0x, moving achieves the same end result. Callingstd::swapwould generate:Which translates to:
The compiler will of course get rid of redundant assignment’s, leaving:
Which is exactly what our custom
swapwould have made in the first place. So while prior to C++0x I would agree with your suggestion, customswap‘s aren’t really necessary anymore, with the introduction of rvalue-references.std::swapwill work perfectly in any class that implements move functions.In fact, I’d argue implementing a
swapfunction should become bad practice. Any class that would need aswapfunction would also need rvalue functions. But in that case, there is simply no need for the clutter of a customswap. Code size does increase (two ravlue functions versus oneswap), but rvalue-references don’t just apply for swapping, leaving us with a positive trade off. (Overall faster code, cleaner interface, slightly more code, no moreswapADL hassle.)As for whether or not we can
defaultrvalue functions, I don’t know. I’ll look it up later or maybe someone else can chime in, but that would sure be helpful. 🙂Even so, it makes sense to allow
defaultrvalue functions instead ofswap. So in essence, as long as they allow= defaultrvalue functions, your request has already been made. 🙂EDIT: I did a bit of searching, and the proposal for
= defaultmove was proposaln2583. According to this (which I don’t know how to read very well), it was “moved back.” It is listed under the section titled “Not ready for C++0x, but open to resubmit in future “. So looks like it won’t be part of C++0x, but may be added later.Somewhat disappointing. 🙁
EDIT 2: Looking around a bit more, I found this: Defining Move Special Member Functions which is much more recent, and does look like we can default
move. Yay!