In the beautiful answer to the copy-and-swap-idiom there is a piece of code I need a bit of help:
class dumb_array
{
public:
// ...
friend void swap(dumb_array& first, dumb_array& second) // nothrow
{
using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
}
// ...
};
and he adds a note
There are other claims that we should specialize std::swap for our type, provide an in-class swap along-side a free-function swap, etc. But this is all unnecessary: any proper use of swap will be through an unqualified call, and our function will be found through ADL. One function will do.
With friend
I am a bit on "unfriendly" terms, I must admit. So, my main questions are:
- looks like a free function, but its inside the class body?
- why isn't this
swap
static? It obviously doesn't use any member variables. - "Any proper use of swap will find out swap via ADL"? ADL will search the namespaces, right? But does it also look inside classes? Or is here where
friend
comes in?
Side-questions:
- With C++11, should I mark my
swap
s withnoexcept
? - With C++11 and its range-for, should I place
friend iter begin()
andfriend iter end()
the same way inside the class? I think thefriend
is not needed here, right?
There are several ways to write
swap
, some better than others. Over time, though, it was found a single definition works best. Let's consider how we might think about writing aswap
function.We first see that containers like
std::vector<>
have a single-argument member functionswap
, such as:Naturally, then, our class should too, right? Well, not really. The standard library has all sorts of unnecessary things, and a member
swap
is one of them. Why? Let's go on.What we should do is identify what's canonical, and what our class needs to do to work with it. And the canonical method of swapping is with
std::swap
. This is why member functions aren't useful: they aren't how we should swap things, in general, and have no bearing on the behavior ofstd::swap
.Well then, to make
std::swap
work we should provide (andstd::vector<>
should have provided) a specialization ofstd::swap
, right?Well that would certainly work in this case, but it has a glaring problem: function specializations cannot be partial. That is, we cannot specialize template classes with this, only particular instantiations:
This method works some of the time, but not all of the time. There must be a better way.
There is! We can use a
friend
function, and find it through ADL:When we want to swap something, we associate†
std::swap
and then make an unqualified call:What is a
friend
function? There is confusion around this area.Before C++ was standardized,
friend
functions did something called "friend name injection", where the code behaved as if if the function had been written in the surrounding namespace. For example, these were equivalent pre-standard:However, when ADL was invented this was removed. The
friend
function could then only be found via ADL; if you wanted it as a free function, it needed to be declared as so (see this, for example). But lo! There was a problem.If you just use
std::swap(x, y)
, your overload will never be found, because you've explicitly said "look instd
, and nowhere else"! This is why some people suggested writing two functions: one as a function to be found via ADL, and the other to handle explicitstd::
qualifications.But like we saw, this can't work in all cases, and we end up with an ugly mess. Instead, idiomatic swapping went the other route: instead of making it the classes' job to provide
std::swap
, it's the swappers' job to make sure they don't use qualifiedswap
, like above. And this tends to work pretty well, as long as people know about it. But therein lies the problem: it's unintuitive to need to use an unqualified call!To make this easier, some libraries like Boost provided the function
boost::swap
, which just does an unqualified call toswap
, withstd::swap
as an associated namespace. This helps make things succinct again, but it's still a bummer.Note that there is no change in C++11 to the behavior of
std::swap
, which I and others mistakenly thought would be the case. If you were bit by this, read here.In short: the member function is just noise, the specialization is ugly and incomplete, but the
friend
function is complete and works. And when you swap, either useboost::swap
or an unqualifiedswap
withstd::swap
associated.†Informally, a name is associated if it will be considered during a function call. For the details, read §3.4.2. In this case,
std::swap
normally isn't considered; but we can associate it (add it to the set of overloads considered by unqualifiedswap
), allowing it to be found.