I am studying this fascinating answer to a subtle question regarding the best practice to implement the swap
function for user-defined types.
It is impossible to overload swap
in namespace std
for a user defined type. Introduction an overload (as opposed to a specialization) in namespace std
is undefined behavior (illegal under the standard, no diagnosis required).
It is impossible to specialize a function in general for a template
class (as opposed to a template
class instance -- ie, std::vector
is an instance, while std::vector
is the entire template
class). What appears to be a specialization is actually an overload. So the first paragraph applies.
The best practice for implementing user-defined swap
is to introduce a swap
function or overload in the same namespace as your template
or class
lives in.
Then, if swap
is called in the right context (using std::swap; swap(a,b);
), which is how it is called in std
library, ADL will kick in, and your overload will be found.
The other option is to do a full specialization of swap
in std
for your particular type. This is impossible (or impractical) for template
classes, as you need to specialize for each and every instance of your template
class that exists. For other classes, it is fragile, as specialization applies to only that particular type: subclasses will have to be respecialized in std
as well.
In general, specialization of functions is extremely fragile, and you are better off introducing overrides. As you cannot introduce overrides into std
, the only place they will be reliably found from is in your own namespace
. Such overrides in your own namespace are preferred over overrides in std
as well.
There are two ways to inject a swap
into your namespace. Both work for this purpose:
namespace test {
struct A {};
struct B {};
void swap(A&, A&) { std::cout << "swap(A&,A&)\n"; }
struct C {
friend void swap(C&, C&) { std::cout << "swap(C&, C&)\n"; }
};
void bob() {
using std::swap;
test::A a, b;
swap(a,b);
test::B x, y;
swap(x, y);
C u, v;
swap(u, v);
}
}
void foo() {
using std::swap;
test::A a, b;
swap(a,b);
test::B x, y;
swap(x, y);
test::C u, v;
swap(u, v);
test::bob();
}
int main() {
foo();
return 0;
}
the first is to inject it into the namespace
directly, the second is to include it as an inline friend
. The inline friend
for "external operators" is a common pattern that basically means you can only find swap
via ADL, but in this particular context does not add much.