views:

2601

answers:

4

std::swap() is used by many std containers (such as std::list and std::vector) during sorting and even assignment.

But the std implementation of swap() is very generalized and rather inefficient for custom types.

Thus efficiency can be gained by overloading std::swap() with a custom type specific implementation. But how can you implement it so it will be used by the std containers?

+23  A: 
Wilka
Mostly good advice, but I have to -1 because of the subtle distinction noted by puetzk between specialising a template in the std namespace (which is allowed by the C++ standard) and overloading (which isn't).
j_random_hacker
Doh! thanks for pointing that out, it's been a long time since I've touched C++ so I'm blaming that for the wrong info there :)
Wilka
A: 

Swap is generally efficient because it swaps only the internal data of the containers. It only swaps some internal pointers that refer to the data (elements, allocator, sorting criterion, if any).

You may want to write your own implementation of swap for your user defined types but why would you want to use that implementation on the standard containers ?

David
+17  A: 

While it's correct that one shouldn't generally add stuff to the std:: namespace, adding template specializations for user-defined types is specifically allowed. Overloading the functions is not. This is a subtle difference :-)

17.4.3.1/1 It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces with namespace std unless otherwise specified. A program may add template specializations for any standard library template to namespace std. Such a specialization (complete or partial) of a standard library results in undefined behaviour unless the declaration depends on a user-defined name of external linkage and unless the template specialization meets the standard library requirements for the original template.

A specialization of std::swap would look like:

namespace std
{
    template<>
    void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}

Without the template<> bit it would be an overload, which is undefined, rather than a specialization, which is permitted. @Wilka's suggest approach of changing the default namespace may work with user code (due to Koenig lookup preferring the namespace-less version) but it's not guaranteed to, and in fact isn't really supposed to (the STL implementation ought to use the fully-qualified std::swap).

There is a thread on comp.lang.c++.moderated with a long dicussion of the topic. Most of it is about partial specialization, though (which there's currently no good way to do).

puetzk
+5  A: 

The right way to overload swap is to write it in the same namespace as what you're swapping, so that it can be found via argument-dependent lookup (ADL). One particularly easy thing to do is:

class X
{
    // ...
    friend void swap(X& a, X& b)
    {
        using std::swap; // bring in swap for built-in types

        swap(a.base1, b.base1);
        swap(a.base2, b.base2);
        // ...
        swap(a.member1, b.member1);
        swap(a.member2, b.member2);
        // ...
    }
};
Dave Abrahams
That's the cleanest way to provide a user defined swap, but it doesn't help in the scenario the questioner is asking about, as std::sort doesn't use ADL to find swap.
Joe Gauterin
In C++2003 it's at best underspecified. Most implementations do use ADL to find swap, but no it's not mandated, so you can't count on it.You *can* specialize std::swap for a specific concrete type as shown by the OP; just don't expect that specialization to get used, e.g. for derived classes of that type.
Dave Abrahams