Why aren’t there compiler-generated swap() methods in C++0x?

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!

Leave a Comment