128

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?

sbi
  • 219,715
  • 46
  • 258
  • 445
Adam
  • 25,966
  • 23
  • 76
  • 87

4 Answers4

150

The right way to overload std::swap's implemention (aka specializing it), 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);
        // ...
    }
};
Top-Master
  • 7,611
  • 5
  • 39
  • 71
Dave Abrahams
  • 7,416
  • 5
  • 31
  • 19
  • 1
    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. – JoeG May 23 '10 at 17:08
  • 12
    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 Jun 01 '10 at 15:52
  • 18
    I would be surprised to find that implementations *still* don't use ADL to find the correct swap. This is an *old* issue on the committee. If your implementation doesn't use ADL to find swap, file a bug report. – Howard Hinnant Feb 23 '11 at 01:32
  • 1
    Somebody should check the library that ships with MSVC. I know they were a holdout for a long time on this one. – Dave Abrahams Feb 24 '11 at 20:47
  • I don't think this is a good idea. First of all: Why defining this function at global scope only? There's no reason why there shouldn't be a member function, too. Secondly, this code means that we have two implementations of swap for 'X'. There is the default one from std namespace and this one. – 0xbadf00d Apr 14 '11 at 10:53
  • 3
    @Sascha: First, I'm defining the function at namespace scope because that's the only kind of definition that matters to generic code. Because int et. al. don't/can't have member functions, std::sort et. al. have to use a free function; they establish the protocol. Second, I don't know why you object to having two implementations, but most classes are doomed to being sorted inefficiently if you can't accept having a non-member swap. Overloading rules ensure that if both declarations are seen, the more specific one (this one) will be chosen when swap is called without qualification. – Dave Abrahams Apr 17 '11 at 14:24
  • 1
    @Howard Hinnant: If your standard library implementation uses ADL to find a function called `swap`, then it's broken. An implementation of `std::sort` must meet the requirements set out in the standard regardless of whether a function called `swap` exists in a user defined namespace (but if a user specializes `std::swap` then that specialization must meet the requirements of `std::swap` otherwise they invoke undefined behaviour). – JoeG Sep 23 '11 at 16:08
  • 3
    Above comment refers to C++98 and C++03 - C++11 provides requirements for swappable types in section 17.6.3.2. – JoeG Sep 23 '11 at 16:15
  • @JoeGauterin: Just tested this with gcc, its std::sort used a swap method defined in this way. Does that mean gcc is non-compliant on this point? – voltrevo Dec 08 '11 at 12:19
  • -1 because a client of my class Foo who wants to use swap is likely to use std::swap(foo1, foo2) which will silently use the inefficient default method. – voltrevo Dec 08 '11 at 12:43
  • 5
    @Mozza314: It depends. A `std::sort` that uses ADL to swap elements is non-conforming C++03 but conforming C++11. Also, why -1 an answer based on the fact that clients might use non-idiomatic code? – JoeG Dec 08 '11 at 13:10
  • 1
    Well I used it on gcc 4.4 without -std=c++0x, so I guess it's non-compliant. I find that strange though, since gcc is highly conforming and I didn't know of any case other than the exemption of export. Also, I'm curious about how idiomatic `using std::swap; swap(foo1, foo2);` is since it is out-of-step with the vast majority of code that uses std::cout, std::pow etc. and so this quirk would only be known to those who have specifically researched it. I'm a trainee C++ developer and I didn't know it, and I just did a straw poll of a couple of my colleagues and they didn't know it either. – voltrevo Dec 08 '11 at 23:49
  • 1
    @Mozza314: I wouldn't call it non-compliant. Since it was underspecified, using ADL here is a conforming extension. Not sure what to say about your curiosity; that's the right idiom to use unless you want to grab a nice wrapper like http://www.boost.org/doc/libs/release/libs/utility/swap.html, which can be called with qualification (boost::swap(x,y)) and will use ADL internally. – Dave Abrahams Dec 11 '11 at 17:19
  • @DaveAbrahams How what this ever "underspecified"? The standard just never allowed it! What is not allowed is forbidden. – curiousguy Jul 03 '13 at 01:52
  • 5
    @curiousguy: If reading the standard was just a simple matter of reading the standard, you’d be right :-). Unfortunately, the intent of the authors matters. So if the original intent was that ADL could or should be used, it’s underspecified. If not, then it’s just a plain old breaking change for C++0x, which is why I wrote “at best” underspecified. – Dave Abrahams Jul 24 '13 at 23:09
  • @DaveAbrahams How do you know the intent of people? Do you have a source? (the script of a debate, a discussion thread, a resolution, a design paper can indicate intent) – curiousguy Jul 26 '13 at 22:40
  • 4
    @curiousguy Yes, he does have a source! He is [Dave Abrahams](https://en.wikipedia.org/wiki/David_Abrahams_(computer_programmer)) – Paolo M Nov 11 '15 at 10:53
80

Attention Mozza314

Here is a simulation of the effects of a generic std::algorithm calling std::swap, and having the user provide their swap in namespace std. As this is an experiment, this simulation uses namespace exp instead of namespace std.

// simulate <algorithm>

#include <cstdio>

namespace exp
{

    template <class T>
    void
    swap(T& x, T& y)
    {
        printf("generic exp::swap\n");
        T tmp = x;
        x = y;
        y = tmp;
    }

    template <class T>
    void algorithm(T* begin, T* end)
    {
        if (end-begin >= 2)
            exp::swap(begin[0], begin[1]);
    }

}

// simulate user code which includes <algorithm>

struct A
{
};

namespace exp
{
    void swap(A&, A&)
    {
        printf("exp::swap(A, A)\n");
    }

}

// exercise simulation

int main()
{
    A a[2];
    exp::algorithm(a, a+2);
}

For me this prints out:

generic exp::swap

If your compiler prints out something different then it is not correctly implementing "two-phase lookup" for templates.

If your compiler is conforming (to any of C++98/03/11), then it will give the same output I show. And in that case exactly what you fear will happen, does happen. And putting your swap into namespace std (exp) did not stop it from happening.

Dave and I are both committee members and have been working this area of the standard for a decade (and not always in agreement with each other). But this issue has been settled for a long time, and we both agree on how it has been settled. Disregard Dave's expert opinion/answer in this area at your own peril.

This issue came to light after C++98 was published. Starting about 2001 Dave and I began to work this area. And this is the modern solution:

// simulate <algorithm>

#include <cstdio>

namespace exp
{

    template <class T>
    void
    swap(T& x, T& y)
    {
        printf("generic exp::swap\n");
        T tmp = x;
        x = y;
        y = tmp;
    }

    template <class T>
    void algorithm(T* begin, T* end)
    {
        if (end-begin >= 2)
            swap(begin[0], begin[1]);
    }

}

// simulate user code which includes <algorithm>

struct A
{
};

void swap(A&, A&)
{
    printf("swap(A, A)\n");
}

// exercise simulation

int main()
{
    A a[2];
    exp::algorithm(a, a+2);
}

Output is:

swap(A, A)

Update

An observation has been made that:

namespace exp
{    
    template <>
    void swap(A&, A&)
    {
        printf("exp::swap(A, A)\n");
    }

}

works! So why not use that?

Consider the case that your A is a class template:

// simulate user code which includes <algorithm>

template <class T>
struct A
{
};

namespace exp
{

    template <class T>
    void swap(A<T>&, A<T>&)
    {
        printf("exp::swap(A, A)\n");
    }

}

// exercise simulation

int main()
{
    A<int> a[2];
    exp::algorithm(a, a+2);
}

Now it doesn't work again. :-(

So you could put swap in namespace std and have it work. But you'll need to remember to put swap in A's namespace for the case when you have a template: A<T>. And since both cases will work if you put swap in A's namespace, it is just easier to remember (and to teach others) to just do it that one way.

Andrea Corbellini
  • 17,339
  • 3
  • 53
  • 69
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 4
    Thankyou very much for the detailed answer. I am clearly less knowledgeable about this and was actually wondering how overloading and specialisation could produce different behaviour. However, I'm not suggesting overloading but specialisation. When I put `template <>` in your first example I get output `exp::swap(A, A)` from gcc. So, why not prefer specialisation? – voltrevo Dec 09 '11 at 00:26
  • 1
    Wow! This is really enlightening. You have definitely convinced me. I think I will slightly modify your suggestion and use the in-class friend syntax from Dave Abrahams (hey I can use this for operator<< too! :-) ), unless you have a reason to avoid that as well (other than compiling separately). Also, in light of this, do you think `using std::swap` is an exception to the "never put using statements inside header files" rule? In fact, why not put `using std::swap` inside ``? I suppose it could break a tiny minority of people's code. Maybe deprecate support and eventually put it in? – voltrevo Dec 09 '11 at 01:07
  • 3
    in-class friend syntax should be fine. I would try to limit `using std::swap` to function scope within your headers. Yes, `swap` is almost a keyword. But no, it is not quite a keyword. So best not to export it to all namespaces until you really have to. `swap` is much like `operator==`. The biggest difference is that no ever even thinks of calling `operator==` with qualified namespace syntax (it would just be too ugly). – Howard Hinnant Dec 09 '11 at 01:17
  • But if exporting swap to all namespaces causes something to break, that something was bad practice, exposing it so it can be fixed. That's a good thing right? – voltrevo Dec 09 '11 at 01:21
  • C++ is a living, evolving language. Maybe what you propose will be acceptable to the community in another 10 years. After all, that's about how long it took to get where we are today. ;-) – Howard Hinnant Dec 09 '11 at 01:30
  • "_If your compiler is conforming (to any of C++98/03/11), then it will give the same output I show._" How can you claim that, when C++98 is silent on the issue? – curiousguy Jul 03 '13 at 02:32
  • @curiousguy: Your claim is that C++98 was silent on the issue of "two-phase lookup" for templates?! – Howard Hinnant Jul 03 '13 at 03:12
  • @HowardHinnant For qualified names, obviously. Unqualified names are covered. – curiousguy Jul 03 '13 at 03:20
  • 16
    @NielKirk: What you are seeing as complication is simply too many wrong answers. There is nothing complicated about Dave Abrahams' correct answer: "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)." – Howard Hinnant Oct 02 '13 at 14:23
  • 1
    See http://stackoverflow.com/questions/21384604/why-will-two-phase-lookup-fail-to-choose-overloaded-version-of-swap - I've asked a StackOverflow question regarding *specifically* this answer. – Dan Nissenbaum Jan 27 '14 at 15:47
  • There *is* something complicated about this... you have to find this post to (a) know this solution is the currently recommended one (b) lose your fear of unknown consequences (c) believe in it. – codeshot Aug 22 '15 at 20:50
  • 3
    @codeshot: Sorry. Herb has been trying to get this message across since 1998: http://www.gotw.ca/publications/mill02.htm He doesn't mention swap in this article. But this is just another application of Herb's Interface Principle. – Howard Hinnant Aug 22 '15 at 21:20
  • 1
    guru's are only a tiny part of the pool. stack overflow, google search with poorly formed queries, etc are how people get things done. I've been working in the software engineering industry for over 10 years and my colleagues (who I mostly learned from) didn't know this, didn't teach it, and (strongly and firmly) nudged in a different direction. – codeshot Aug 22 '15 at 22:20
  • I get "exp::swap(A, A)" when using the the `template struct A` example. – Pharap May 04 '18 at 17:27
  • @Pharap: Visual Studio? – Howard Hinnant May 04 '18 at 18:27
  • @HowardHinnant Yes. – Pharap May 05 '18 at 11:00
  • 2
    Visual Studio does not yet correctly implement the 2-phase lookup rules introduced in C++98. That means that in this example VS calls the wrong `swap`. This adds a new wrinkle I hadn't previously considered: In the case of `template struct A`, putting your `swap` into namespace `std` renders your code non-portable. Try your example out on wandbox to see how gcc and clang handle it. – Howard Hinnant May 05 '18 at 15:59
  • @Pharap To make the long story short: Visual Studio C++ compiler's internal design made implementing this properly essentially impossible, and they kept it broken as long as they could, and only in the last few years their compiler team changed direction and took standards compliance seriously. I mean, how boneheaded can you be to knowingly not implement a rather fundamental part of C++ standard for ąpprox. two decades? MSVC used to be a C++ pariah. It's only recently that it's on par with open source compilers in terms of ability to deal with standard C++. – Kuba hasn't forgotten Monica Dec 07 '20 at 04:24
  • In the first example, is `begin` a dependent name or a non dependent name? I'm trying to understand two phase lookup and I think `begin` should be a dependent name so the actual instantiation should use the specialized overload? – PYA Dec 23 '21 at 16:10
55

You're not allowed (by the C++ standard) to overload std::swap, however you are specifically allowed to add template specializations for your own types to the std namespace. E.g.

namespace std
{
    template<>
    void swap(my_type& lhs, my_type& rhs)
    {
       // ... blah
    }
}

then the usages in the std containers (and anywhere else) will pick your specialization instead of the general one.

Also note that providing a base class implementation of swap isn't good enough for your derived types. E.g. if you have

class Base
{
    // ... stuff ...
}
class Derived : public Base
{
    // ... stuff ...
}

namespace std
{
    template<>
    void swap(Base& lha, Base& rhs)
    {
       // ...
    }
}

this will work for Base classes, but if you try to swap two Derived objects it will use the generic version from std because the templated swap is an exact match (and it avoids the problem of only swapping the 'base' parts of your derived objects).

NOTE: I've updated this to remove the wrong bits from my last answer. D'oh! (thanks puetzk and j_random_hacker for pointing it out)

Dan Nissenbaum
  • 13,558
  • 21
  • 105
  • 181
Wilka
  • 28,701
  • 14
  • 75
  • 97
  • 14
    Downvoted because the correct way to customize swap is to do so in your own namespace (as Dave Abrahams points out in another answer). – Howard Hinnant Feb 23 '11 at 01:30
  • Is it forbidden to overload `std::swap` (or anything else), but outside of `std::swap` namespace? – Kos Jul 28 '11 at 14:07
  • 15
    @HowardHinnant, Dave Abrahams: I disagree. On what basis do you claim your alternative is the "correct" way? As puetzk quoted from the standard, this is specifically allowed. While I'm new to this issue I really don't like the method you advocate because if I define Foo and swap that way someone else who uses my code is likely to use std::swap(a, b) rather than swap(a, b) on Foo, which silently uses the inefficient default version. – voltrevo Dec 08 '11 at 12:39
  • 5
    @Mozza314: The space and formatting constraints of the comment area did not allow me to fully reply to you. Please see the answer I've added titled "Attention Mozza314". – Howard Hinnant Dec 08 '11 at 23:53
  • 1
    @HowardHinnant, am I right in thinking this technique could also easily breach the one-definition-rule? If a translation unit has included and a forward declaration of class Base; whilst another includes the header, above, then you have two different instances of std::swap. I recall this is forbidden in a conforming program but using this technique means you must successfully prevent users of your class from writing a forward declaration - they must be forced somehow to always include your header to achieve their goals. This turns out to be impractical to achieve at scale. – codeshot Nov 06 '16 at 15:25
  • I believe starting with C++20 it is only correct to specialize template classes, not template functions. – Chris_F Jun 25 '19 at 11:52
31

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
  • 10,534
  • 3
  • 28
  • 32
  • 7
    One reason it's wrong to use function template specialization for this (or anything): it interacts in bad ways with overloads, of which there are many for swap. For example, if you specialize the regular std::swap for std::vector&, your specialization won't get chosen over the standard's vector-specific swap, because specializations aren't considered during overload resolution. – Dave Abrahams Feb 24 '11 at 20:46
  • 4
    This is also what Meyers recommends in Effective C++ 3ed (Item 25, pp 106-112). – jww Sep 05 '11 at 16:28
  • 2
    @DaveAbrahams: If you specialize (without explicit template arguments), partial ordering will cause it to be a specialization _of_ the `vector` version and it [will be used](https://wandbox.org/permlink/dmHLwU5uxmFKtwDw). – Davis Herring Mar 13 '18 at 13:54
  • 1
    @DavisHerring actually, no, when you do that partial ordering plays no role. The problem isn't that you can't call it at all; it's what happens in the presence of apparently-less-specific overloads of swap: https://wandbox.org/permlink/nck8BkG0WPlRtavV – Dave Abrahams Mar 15 '18 at 15:20
  • 2
    @DaveAbrahams: The partial ordering is to [select the function template to specialize](http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading) when the explicit specialization matches more than one. The `::swap` overload you added is more specialized than the `std::swap` overload for `vector`, so it captures the call and no specialization of the latter is relevant. I’m not sure how that’s a practical problem (but neither am I claiming that this is a good idea!). – Davis Herring Mar 15 '18 at 21:50
  • @DavisHerring, ah, OK, yes; I didn't recall that there was a `swap` overload for std::vector. It's a practical problem because the programmer thinks he's customizing the behavior of `swap` for a specific type, but his customization can be rendered ineffective by a less-specific overload. – Dave Abrahams Mar 16 '18 at 23:31