How to overload std::swap()
Solution 1
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);
// ...
}
};
Solution 2
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.
Solution 3
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)
Solution 4
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).
Comments
-
Adam over 2 years
std::swap()
is used by many std containers (such asstd::list
andstd::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? -
JoeG almost 14 yearsThat'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.
-
Dave Abrahams almost 14 yearsIn 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.
-
Howard Hinnant about 13 yearsDownvoted 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 about 13 yearsI 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.
-
Dave Abrahams about 13 yearsOne 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<mytype>&, your specialization won't get chosen over the standard's vector-specific swap, because specializations aren't considered during overload resolution.
-
Dave Abrahams about 13 yearsSomebody should check the library that ships with MSVC. I know they were a holdout for a long time on this one.
-
0xbadf00d about 13 yearsI 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.
-
Dave Abrahams about 13 years@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.
-
Kos over 12 yearsIs it forbidden to overload
std::swap
(or anything else), but outside ofstd::swap
namespace? -
jww over 12 yearsThis is also what Meyers recommends in Effective C++ 3ed (Item 25, pp 106-112).
-
JoeG over 12 years@Howard Hinnant: If your standard library implementation uses ADL to find a function called
swap
, then it's broken. An implementation ofstd::sort
must meet the requirements set out in the standard regardless of whether a function calledswap
exists in a user defined namespace (but if a user specializesstd::swap
then that specialization must meet the requirements ofstd::swap
otherwise they invoke undefined behaviour). -
JoeG over 12 yearsAbove comment refers to C++98 and C++03 - C++11 provides requirements for swappable types in section 17.6.3.2.
-
voltrevo over 12 years@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 over 12 years@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 over 12 years-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.
-
JoeG over 12 years@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? -
voltrevo over 12 yearsWell 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. -
Howard Hinnant over 12 years@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".
-
voltrevo over 12 yearsThankyou 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 outputexp::swap(A, A)
from gcc. So, why not prefer specialisation? -
voltrevo over 12 yearsWow! 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 putusing std::swap
inside<algorithm>
? I suppose it could break a tiny minority of people's code. Maybe deprecate support and eventually put it in? -
Howard Hinnant over 12 yearsin-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 likeoperator==
. The biggest difference is that no ever even thinks of callingoperator==
with qualified namespace syntax (it would just be too ugly). -
voltrevo over 12 yearsBut 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?
-
Howard Hinnant over 12 years<shrug> 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. ;-)
-
Dave Abrahams over 12 years@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 boost.org/doc/libs/release/libs/utility/swap.html, which can be called with qualification (boost::swap(x,y)) and will use ADL internally.
-
curiousguy almost 11 years@DaveAbrahams How what this ever "underspecified"? The standard just never allowed it! What is not allowed is forbidden.
-
curiousguy almost 11 years"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?
-
Howard Hinnant almost 11 years@curiousguy: Your claim is that C++98 was silent on the issue of "two-phase lookup" for templates?!
-
curiousguy almost 11 years@HowardHinnant For qualified names, obviously. Unqualified names are covered.
-
Dave Abrahams almost 11 years@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.
-
curiousguy almost 11 years@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)
-
Howard Hinnant over 10 years@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)."
-
Dan Nissenbaum about 10 yearsSee stackoverflow.com/questions/21384604/… - I've asked a StackOverflow question regarding specifically this answer.
-
codeshot over 8 yearsThere 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.
-
Howard Hinnant over 8 years@codeshot: Sorry. Herb has been trying to get this message across since 1998: gotw.ca/publications/mill02.htm He doesn't mention swap in this article. But this is just another application of Herb's Interface Principle.
-
codeshot over 8 yearsguru'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.
-
Paolo M over 8 years@curiousguy Yes, he does have a source! He is Dave Abrahams
-
codeshot over 7 years@HowardHinnant, am I right in thinking this technique could also easily breach the one-definition-rule? If a translation unit has included <algorithm> and a forward declaration of class Base; whilst another includes the header, above, then you have two different instances of std::swap<Base>. 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.
-
Davis Herring about 6 years@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. -
Dave Abrahams about 6 years@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: wandbox.org/permlink/nck8BkG0WPlRtavV
-
Davis Herring about 6 years@DaveAbrahams: The partial ordering is to select the function template to specialize when the explicit specialization matches more than one. The
::swap
overload you added is more specialized than thestd::swap
overload forvector
, 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!). -
Dave Abrahams about 6 years@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 ofswap
for a specific type, but his customization can be rendered ineffective by a less-specific overload. -
Pharap almost 6 yearsI get "exp::swap(A, A)" when using the the
template<class T> struct A
example. -
Howard Hinnant almost 6 years@Pharap: Visual Studio?
-
Pharap almost 6 years@HowardHinnant Yes.
-
Howard Hinnant almost 6 yearsVisual 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 oftemplate<class T> struct A
, putting yourswap
into namespacestd
renders your code non-portable. Try your example out on wandbox to see how gcc and clang handle it. -
Chris_F almost 5 yearsI believe starting with C++20 it is only correct to specialize template classes, not template functions.
-
Kuba hasn't forgotten Monica over 3 years@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++.
-
PYA over 2 yearsIn the first example, is
begin
a dependent name or a non dependent name? I'm trying to understand two phase lookup and I thinkbegin
should be a dependent name so the actual instantiation should use the specialized overload? -
Luis Ayuso about 2 yearsone question. what is the purpose of friend here?