How Does "Using Std::Swap" Enable Adl

How does using std::swap enable ADL?

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.

using std::swap disabled ADL

In overload resolution your swap will loose against the std::swap one, because std::swap takes the arguments as non-const references, while your function takes them as const references, making it a worse match.

Just remove the const in the arguments and your ADL overload will be preferred.

In practical terms a swap function taking const arguments doesn't make much sense anyway.

what does `using std::swap` inside the body of a class method implementation mean?

This mechanism is normally used in templated code, i.e. template <typename Value> class Foo.

Now the question is which swap to use. std::swap<Value> will work, but it might not be ideal. There's a good chance that there's a better overload of swap for type Value, but in which namespace would that be? It's almost certainly not in std:: (since that's illegal), but quite likely in the namespace of Value. Likely, but far from certain.

In that case, swap(myValue, anotherValue) will get you the "best" swap possible. Argument Dependent Lookup will find any swap in the namespace where Value came from. Otherwise the using directive kicks in, and std::swap<Value> will be instantiated and used.

In your code, mSize is likely an integral type, and mArray a pointer. Neither has an associated namespace, and std::swap is with 99.9% certainty optimal for them anyway. Therefore, the using std::swap; declaration seems useless here.

Does C++11 change the behavior of explicitly calling std::swap to ensure ADL-located swap's are found, like boost::swap?

I would have had to vote against your proof-of-concept implementation had it been proposed. I fear it would break the following code, which I'm pretty sure I've seen in the wild at least once or twice over the past dozen years.

namespace oops
{

struct foo
{
foo() : i(0) {}
int i;

void swap(foo& x) {std::swap(*this, x);}
};

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

}

Whether you think the above is good code or bad, it works as the author intends in C++98/03 and so the bar for silently breaking it is pretty high. Telling users that in C++11 they would no longer have to write using std::swap; isn't a sufficiently high benefit to outweigh the disadvantage of silently turning the above code into infinite recursion.

Another way to get out of writing using std::swap; is to use std::iter_swap instead:

template <typename T>
void do_swap(T& lhs, T& rhs)
{
std::iter_swap(&lhs, &rhs); // internally does what do_swap did above
}

The c++ using statement within a function, followed by a function name (for ADL?)

The using statement makes this line work:

swap(first, second);

Notice that we can omit std:: in front of swap.

The important thing there is that std::swap(...) is a qualified lookup, but swap(...) is an unqualified lookup. The main difference is that qualified lookup is calling a function in a specific namespace or scope (the one specified), whereas unqualified lookup is a bit more flexible since it will look into parent scope of the current context and also the global namespace. In addition, unqualified lookup will also look into the scope of the type of the arguments. It's a nice tool, but also dangerous since it can call function from unexpected places.

ADL will only work with unqualified lookup, since it has to search for other namespaces and scopes.

The using std::swap also ensure that if no function is found through ADL, it will call std::swap by default.

This idiom allow for user defined swap functions:

struct MyType {
// Function found only through ADL
friend void swap(MyType& l, MyType& r) {
// ...
}
};

using namespace std causes boost pointer cast to trigger ADL in c++17 standard

When the compiler sees a < after an identifier in an expression (such as in static_pointer_cast<Animal>(dp)) it needs to figure out whether < refers to the relational less operator or whether it is the start of a template argument list.

If a template is found by usual unqualified name lookup (not ADL), then the latter is assumed to be the case. But what if no template is found?

Before C++20, if the lookup did not find a template, it was assumed that the name does not refer to a template and that the following < is therefore the less operator.

In your original case that wouldn't make sense. For example there is nothing that static_pointer_cast can refer to as operand to <. So it will fail with a more or less clear error message depending on the compiler.

With using namespace std; it may be possible that the compiler implicitly included std::static_pointer_cast (from <memory>) with one of the headers you included explicitly (there is no guarantee) or it may be that the boost headers include <memory>. If that happened, then the unqualified lookup for static_pointer_cast will find it and because it is a template, < after it will be considered to start a template argument list instead of being the relation operator. Then it is clear that the whole expression is an unqualified function call with template argument list.

Since C++20, if the lookup does not find anything or finds any function at all, then it is also assumed that a < following the name introduces a template argument list.

With the interpretation as template argument list and therefore function call, normal unqualified name lookup and ADL for unqualified function calls will be done, which finds both boost::shared_pointer_cast (via ADL) and std::shared_pointer_cast if you are using using namespace std;, but because you are passing a boost::shared_ptr the std version, which requires a std::shared_ptr argument, will not be viable and the boost version will always be chosen by overload resolution. This part is unchanged with C++20.

Note that this is a breaking change in C++20. In some unusual situations you might have wanted < after a function name to actually refer to the relational operator which would now require parenthesizing the function name. See [diff.cpp17.temp] for an example.

What's the difference between std::ranges::swap() and std::swap()?

In rough terms: std::ranges::swap generalizes std::swap.

  • If T and U are the same, then it's equivalent to invoking std::swap.
  • Otherwise it will swap the arguments as two ranges - if they're ranges.
  • Otherwise it will perform a swap-like procedure with move assignment between the different types T and U.
  • If even that can't happen, it will fail to compile.

See the cppreference page for details.

I must say I find this function to be somewhat confusing and baroque, but then - maybe people more experienced with the range library develop an intuition as to why it makes sense.

Why/when should we prefer using std::swap; swap(a, b) over std::iter_swap(&a, &b)?

3 years later, but I believe the only technical concern would be if operator &() is overloaded.

If not, the only other reason I see would be readability.

Why are there so many specializations of std::swap?

So what it is gained from specializing std::pair?

Performance. The generic swap is usually good enough (since C++11), but rarely optimal (for std::pair, and for most other data structures).

I'm also left wondering if I should be writing my own specializations for custom classes, or simply relying on the template version.

I suggest relying on the template by default, but if profiling shows it to be a bottleneck, know that there is probably room for improvement. Premature optimization and all that...

How does ADL affect this piece of C++ code?

In this case, normal name lookup finds N2::foo, and N1::foo is found by ADL, they're both added to the overload set, then overload resolution is performed and the calling is ambiguous.

BTW: Without using N2::foo; in main(), ::foo will be found by normal name lookup, and N1::foo is found by ADL too; as the result the calling is still ambiguous.

Updated:

So, the question here is why ::foo can not be called by "foo(N1::S{});" in the main function?

Because with the usage of using N2::foo;, the name N2::foo is introduced in the main function. When calling foo the name N2::foo will be found at the scope of main, then name lookup stops, the further scope (the global namespace) won't be examined, so ::foo won't be found and added to overload set at all. As the result N2::foo is called for both cases.

name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.

BTW: If you put using N2::foo; in global namespace before main, foo(N1::S{}); would call ::foo. Both N2::foo and ::foo are found by name lookup and ::foo wins in overload resolution.

LIVE



Related Topics



Leave a reply



Submit