How to Overload Std::Swap()

How to overload std::swap()

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);
// ...
}
};

C++ overload of swap function not working

You have to uses std::ranges::swap, or find it via ADL if you aren't using C++20:

myname::Grid<int> grid1(10, 10);
myname::Grid<int> grid2(10, 10);

std::ranges::swap(grid1, grid2);

// Or with ADL
using std::swap;
swap(grid1, grid2);

"Swapping" does not exactly mean std::swap, but this form of "using std::swap; swap(x, y);", which is encapsulated by std::ranges::swap.


Writing std::swap(grid1, grid2) directly calls the default template<typename T> void std::move(T&, T&);. This will use the move construct and move assignment operators instead of your custom swap function, which is why you don't see "Custom Swap Used".

Should you overload swap in the std namespace?

You're doing it wrong :)

17.6.2.4.1 [namespace.std]

  1. The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

That pretty clearly says you may not add overloads to namespace std. You could specialize std::swap<MyType> for your type, but if your type is a template you'd need a partial specialization, std::swap<MyContainer<T>> and you can't partially specialize a function template, so that won't work, so it's not a good approach in general.

C++11 also defines the requirements for a type to be swappable which include:

17.6.3.2 [swappable.requirements]

  1. ...
  2. ...
  3. The context in which swap(t, u) and swap(u, t) are evaluated shall ensure that a binary non-member function named "swap" is selected via overload resolution (13.3) on a candidate set that includes:

  • the two swap function templates defined in <utility> (20.2) and
  • the lookup set produced by argument-dependent lookup (3.4.2).

So calling swap on two objects of swappable type should be able to find std::swap and should be able to find other overloads by ADL. Calling it unqualified (and without an explicit template argument list) ensures ADL happens, and including <utility> and adding the using-declaration for std::swap ensures the standard overloads can be found. So doing it the way you show in your question meets those requirements.

That pretty clearly defines what it takes to be swappable in the sense used by the standard, which is what is required by the standard library e.g. by the functions in <algorithm>.

If you put swap overloads for your type in your type's namespace then they can be found by ADL. That is the right thing to do anyway, functions related to your type belong in the same namespace as your type, see Item 57 in C++ Coding Standards by Sutter and Alexandrescu for more details on that topic.

So in short, you have been doing it wrong. What you read is correct. Doing using std::swap and relying on ADL always works (for templates and non-templates) and avoids undefined behaviour. Yay.

N.B. The C++03 standard was less clear about how user-defined types should be swapped. For some history around this area see N1691 2.2 which defines the term customization point and shows different ways to define them in APIs. The protocol used in C++11 for swapping types uses one of those ways, and is now clearly and unambiguously blessed as the "correct way" to provide a swap function for your type. Other customization points in other libraries can use other approaches, but to be swappable in C++11 terms means using std::swap; and relying on ADL.

Overloading global swap for user-defined type

What you have is not a specialization, it is overloading and exactly what the standard prohibits. (However, it will almost always currently work in practice, and may be acceptable to you.)

Here is how you provide your own swap for your class template:

template<class T>
struct Ex {
friend void swap(Ex& a, Ex& b) {
using std::swap;
swap(a.n, b.n);
}
T n;
}

And here is how you call swap, which you'll notice is used in Ex's swap too:

void f() {
using std::swap; // std::swap is the default or fallback
Ex<int> a, b;
swap(a, b); // invokes ADL
}

Related: Function template specialization importance and necessity



Related Topics



Leave a reply



Submit