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:
swap does not throw, it must be implemented for all non-POD members in the class and in base classes, in their non-POD members, etc.swap method. Failing to do so can introduce subtle bugs. Also, since swap is an ordinary method, compilers (at least none I know of) don't emit warnings if the swap implementation 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:
swap method, an implicit copy-assignment operator can be implemented using copy-and-swap.swap method, 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 swap functions in C++ prior to 0x is because the general free-function std::swap was 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, swap is implemented as:
template <typename T> void swap(T& x, T& y) { T temp(std::move(x)); x = std::move(y); y = std::move(temp); } 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 swap will essentially do what any custom written swap function would do. Consider a class we'd normally write swap for (such as this "dumb" vector):
struct dumb_vector { int* pi; // lots of allocated ints // constructors, copy-constructors, move-constructors // copy-assignment, move-assignment }; Previously, swap would make a redundant copy of all our data, before discarding it later. Our custom swap function would just swap the pointer, but can be clumsy to use in some cases. In C++0x, moving achieves the same end result. Calling std::swap would generate:
dumb_vector temp(std::move(x)); x = std::move(y); y = std::move(temp); Which translates to:
dumb_vector temp; temp.pi = x.pi; x.pi = 0; // temp(std::move(x)); x.pi = y.pi; y.pi = 0; // x = std::move(y); y.pi = temp.pi; temp.pi = 0; // y = std::move(temp); The compiler will of course get rid of redundant assignment's, leaving:
int* temp = x.pi; x.pi = y.pi; y.pi = temp; Which is exactly what our custom swap would have made in the first place. So while prior to C++0x I would agree with your suggestion, custom swap's aren't really necessary anymore, with the introduction of rvalue-references. std::swap will work perfectly in any class that implements move functions.
In fact, I'd argue implementing a swap function should become bad practice. Any class that would need a swap function would also need rvalue functions. But in that case, there is simply no need for the clutter of a custom swap. Code size does increase (two ravlue functions versus one swap), 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 more swap ADL hassle.)
As for whether or not we can default rvalue 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 default rvalue functions instead of swap. So in essence, as long as they allow = default rvalue functions, your request has already been made. :)
EDIT: I did a bit of searching, and the proposal for = default move was proposal n2583. 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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With