How does "using std::swap" enable Argument-Dependent Lookup (ADL)?
Asked Answered
S

2

31

In What is the copy-and-swap idiom this example is shown:

friend void swap(dumb_array& first, dumb_array& second) // nothrow
{
    // enable ADL (not necessary in our case, but good practice)
    using std::swap; 

    // by swapping the members of two classes,
    // the two classes are effectively swapped
    swap(first.mSize, second.mSize); 
    swap(first.mArray, second.mArray);
}

How exactly does using std::swap enable ADL? ADL only requires an unqualified name. The only benefits I see for using std::swap is that since std::swap is a function template you can use a template argument list in the call (swap<int, int>(..)).

If that is not the case then what is using std::swap for?

Stuartstub answered 24/1, 2015 at 21:52 Comment(1)
While reading around for this, I also found this blog entry helpful.Seibert
P
21

The "enable ADL" comment applies to the transformation of

std::swap(first.mSize, second.mSize);
std::swap(first.mArray, second.mArray);

to

using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);

You're right, ADL only requires an unqualified name, but this is how the code is re-worked to use an unqualified name.

Just plain

swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);

wouldn't work, because for many types, ADL won't find std::swap, and no other usable swap implementation is in scope.

Phalange answered 24/1, 2015 at 21:56 Comment(2)
You mean using std::swap is a fallback for the types for which there are no associated namespaces or classes?Stuartstub
@templateboy Yes, and also types for which there are associated namespaces, but for which no custom swap function has been provided. The most common types for which that is necessary are all built-in types.Phalange
E
26

Just wanted to add why this idiom is used at all, which seemed like the spirit of the original question.

This idiom is used within many std library classes where swap is implemented. From http://www.cplusplus.com/reference/algorithm/swap/:

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.

So the purpose of using an unqualified "swap" to swap member variables in the function you described is so that ADL can find customized swap functions for those classes (if they exist elsewhere).

Since these customized classes don't exist within the class you're referencing (mSize and mArray is a std::size_t and an int*, respectively, in the original example), and the std::swap works just fine, the author added a comment that this was not necessary in this case, but good practice. He would have gotten the same results had he explicitly called std::swap, as is pointed out in the previous answer.

Why is it good practice? Because if you have as members instances of classes for which custom swap is defined, you want the behavior to be this: check for a customized swap function...if it exists, use it, if it does not exist, use the std library functions. In the cases where there are no customized swap functions available, you want it to default to the simple std::swap implementation described in the link above. Hence the "using", to bring swap for built-in types into the namespace. But those will be tried last.

See also: https://mcmap.net/q/16168/-how-to-overload-std-swap

If for some reason you hate the "using std::swap", I suppose you could in theory resolve this manually by explicitly calling std::swap for everything you'd want to swap using std::swap and using the unqualified swap for every custom swap you know is defined (still found using ADL). But this is error prone ... if you didn't author those classes you may not know if a customized swap exists for it. And switching between std::swap and swap makes for confusing code. Better to let the compiler handle all of this.

Erse answered 17/4, 2015 at 17:48 Comment(0)
P
21

The "enable ADL" comment applies to the transformation of

std::swap(first.mSize, second.mSize);
std::swap(first.mArray, second.mArray);

to

using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);

You're right, ADL only requires an unqualified name, but this is how the code is re-worked to use an unqualified name.

Just plain

swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);

wouldn't work, because for many types, ADL won't find std::swap, and no other usable swap implementation is in scope.

Phalange answered 24/1, 2015 at 21:56 Comment(2)
You mean using std::swap is a fallback for the types for which there are no associated namespaces or classes?Stuartstub
@templateboy Yes, and also types for which there are associated namespaces, but for which no custom swap function has been provided. The most common types for which that is necessary are all built-in types.Phalange

© 2022 - 2024 — McMap. All rights reserved.