98

What is the proper way to enable my swap in STL algorithms?

1) Member swap. Does std::swap use SFINAE trick to use the member swap.

2) Free standing swap in the same namespace.

3) Partial specialization of std::swap.

4) All of the above.

Thank you.

EDIT: Looks like I didn't word my question clearly. Basically, I have a template class and I need STL algos to use the (efficient) swap method I wrote for that class.

xskxzr
  • 12,442
  • 12
  • 37
  • 77
pic11
  • 14,267
  • 21
  • 83
  • 119

3 Answers3

100
  1. is the proper use of swap. Write it this way when you write "library" code and want to enable ADL (argument-dependent lookup) on swap. Also, this has nothing to do with SFINAE.
// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs) {
    using std::swap; // enable 'std::swap' to be found
                    // if no other 'swap' is found through ADL
    // some code ...
    swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                    // or falls back on 'std::swap'
    // more code ...
}
  1. Here is the proper way to provide a swap function for your class:
namespace Foo {

class Bar{}; // dummy

void swap(Bar& lhs, Bar& rhs) {
    // ...
}

}

If swap is now used as shown in 1), your function will be found. Also, you may make that function a friend if you absolutely need to, or provide a member swap that is called by the free function:

// version 1
class Bar{
public:
    friend void swap(Bar& lhs, Bar& rhs) {
    // ....
    }
};

// version 2
class Bar{
public:
    void swap(Bar& other) {
    // ...
    }
};

void swap(Bar& lhs, Bar& rhs) {
    lhs.swap(rhs);
}

...
  1. You mean an explicit specialization. Partial is still something else and also not possible for functions, only structs / classes. As such, since you can't specialize std::swap for template classes, you have to provide a free function in your namespace. Not a bad thing, if I may say so. Now, an explicit specialization is also possible, but generally you do not want to specialize a function template:
namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
    // ...
}

}
  1. No, as 1) is distinct from 2) and 3). Also, having both 2) and 3) will lead to always having 2) picked, because it fits better.
John
  • 2,963
  • 11
  • 33
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 8
    Your (1) and the question's (1) don't really line up, unless I'm misreading something. Still, +1 – Dennis Zickefoose Jun 17 '11 at 03:50
  • @Dennis: To be honest, I can't really make much out of OP's 1), but if he meant what I think, I covered that in 2) (version 2). – Xeo Jun 17 '11 at 04:01
  • So you did, somehow I overlooked that. – Dennis Zickefoose Jun 17 '11 at 04:03
  • 1
    @Xeo. Thanks for you input. I edited my question. Does STL use swap as you described it in case 1? – pic11 Jun 17 '11 at 21:44
  • 1
    @pic: Yes, the STL will use the ADL swap I showed in 1), but only if it is there as a free function, not only a member function. See 2) and 3), both versions will be picked by algorithms. I'd advice 2), as 3) is out-dated and considered bad practice. – Xeo Jun 17 '11 at 22:29
  • 2
    The comment in the first piece of code is misleading. `using std::swap;` does not enable ADL, it just allows the compiler to locate `std::swap` if ADL does not find a proper overload. – David Rodríguez - dribeas Jun 19 '11 at 16:47
  • 1
    note that boost::swap implement this techniques generically and can be used as a drop-in replacement of std::swap to get all the ADL benefit presented before – Joel Falcou Jun 20 '11 at 16:30
  • @Xeo yeah just poiinting out it is comveniently wrapped already ;) – Joel Falcou Jun 20 '11 at 16:34
  • 1
    This doesn't explain *why* an explicit specialization is considered not as good. One advantage of the specialization is that clients don't need to know to use `boost::swap` or to do the `using std::swap` thing for ADL. – jamesdlin Aug 12 '12 at 12:25
  • @jamesdlin: Good point, added a link to Herb's article why function template specializations are bad. – Xeo Aug 12 '12 at 13:22
  • "Also, you may make that function a friend if you absolutely need to", in what case would you *absolutely* need to? (If you need to access private members, is that what you mean?) – alfC Jan 10 '16 at 08:45
  • 6
    This answer is technically correct, but is **sorely** in need of editing for clarity. The OP's (1) is not the correct answer as an overly-quick reading in this answer would seem to mistakenly indicate. – Howard Hinnant Mar 16 '16 at 03:32
  • The one other alternative is to define swap inside class as a friend. The downside is that you can't call it from outside specifying namespace explicitly (only through ADL). The upside is you can use all the private class members without writing 2 definitions + friend declaration. – Predelnik Feb 09 '17 at 15:43
  • 1
    This is a good answer for when it was written, but it could use an update to point out that it might not be necessary to provide a custom swap at all. For many classes, if you provide a noexcept move-constructor and a noexcept move-assignment operator (possible with =default), then `std::swap` can work just fine. – Adrian McCarthy May 15 '20 at 14:58
2

It seems that (2) (free standing swap in the same namespace where the user-defined class is declared) is the only allowed way to provide swap for a user-defined class, because adding declarations to namespace std is generally an undefined behavior. Extending the namespace std (cppreference.com):

It is undefined behavior to add declarations or definitions to namespace std or to any namespace nested within std, with a few exceptions noted below

And swap is not denoted as one of those exceptions. So adding your own swap overload to the std namespace is an undefined behavior.

It's also said that the standard library uses an unqualified call to the swap function in order to call user-defined swap for a user class if such user-defined swap is provided.

Swappable (cppreference.com):

Many standard library functions (for example, many algorithms) expect their arguments to satisfy Swappable, which means that any time the standard library performs a swap, it uses the equivalent of using std::swap; swap(t, u);.

swap (www.cplusplus.com):

Many components of the standard library (within std) call swap in an unqualified manner to allow custom overloads for non-fundamental types to be called instead of this generic version: Custom overloads of swap declared in the same namespace as the type for which they are provided get selected through argument-dependent lookup over this generic version.

But note that directly using the std::swap function for a user-defined class calls the generic version of std::swap instead of the user-defined swap:

my::object a, b;
std::swap(a, b); // calls std::swap, not my::swap

So it is recommended to call the swap function in user code in the same way as it is done in the standard library:

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
anton_rh
  • 8,226
  • 7
  • 45
  • 73
1

To answer the EDIT, where the classes may be template classes, you don't need specialization at all. consider a class like this:

template <class T>
struct vec3
{
    T x,y,z;
};

you may define classes such as:

vec3<float> a;
vec3<double> b;
vec3<int> c;

if you want to be able to create one function to implement all 3 swaps (not that this example class warrants it) you do just like Xeo said in (2)... without specialization but just make a regular template function:

template <class T>
void swap(vec3<T> &a, vec3<T> &b)
{
    using std::swap;
    swap(a.x,b.x);
    swap(a.y,b.y);
    swap(a.z,b.z);
}

The swap template function should be located in the same namespace as the class you're trying to swap. the following method will find and use that swap even though you're not referencing that namespace using ADL:

using std::swap;
swap(a,b);
Ben
  • 843
  • 8
  • 9