Why will two-phase lookup fail to choose overloaded version of 'swap'?

argument-dependent-lookup, c++, c++11, swap, templates

Solution

Preamble with plenty of Standardese

The call to `swap()` in the example entails a dependent name because its arguments `begin[0]` and `begin[1]` depend on the template parameter `T` of the surrounding `algorithm()` function template. Two-phase name lookup for such dependent names is defined in the Standard as follows:

14.6.4.2 Candidate functions [temp.dep.candidate]

1 For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:

— For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition context are found.

— For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.

Unqualified lookup is defined by

3.4.1 Unqualified name lookup [basic.lookup.unqual]

1 In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.

and argument-dependent lookup (ADL) as

3.4.2 Argument-dependent name lookup [basic.lookup.argdep]

1 When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template argument).

Applying the Standard to the example

The first example calls `exp::swap()`. This is not a dependent name and does not require two-phase name lookup. Because the call to swap is qualified, ordinary lookup takes place which finds only the generic `swap(T&, T&)` function template.

The second example (what @HowardHinnant calls "the modern solution") calls `swap()` and also has an overload `swap(A&, A&)` in the same namespace as where `class A` lives (the global namespace in this case). Because the call to swap is unqualified, both ordinary lookup and ADL take place at the point of definition (again only finding the generic `swap(T&, T&)`) but another ADL takes place at the point of instantiation (i.e where `exp::algorithm()` is being called in `main()`) and this picks up `swap(A&, A&)` which is a better match during overload resolution.

So far so good. Now for the encore: the third example calls `swap()` and has a specialization `template<> swap(A&, A&)` inside `namespace exp`. The lookup is the same as in the second example, but now ADL does not pick up the template specialization because it is not in an associated namespace of `class A`. However, even though the specialization `template<> swap(A&, A&)` does not play a role during overload resolution, it is still instantiated at the point of use.

Finally, the fourth example calls `swap()` and has an overload `template<class T> swap(A<T>&, A<T>&)` inside `namespace exp` for `template<class T> class A` living in the global namespace. The lookup is the same as in the third example, and again ADL does not pick up the overload `swap(A<T>&, A<T>&)` because it is not in an associated namespace of the class template `A<T>`. And in this case, there is also no specialization that has to be instantiated at the point of use, so the generic `swap(T&, T&)` is being callled here.

Conclusion

Even though you are not allowed to add new overloads to `namespace std`, and only explicit specializations, it would not even work because of the various intricacies of two-phase name lookup.

Problem

I am studying this fascinating answer to a subtle question regarding the best practice to implement the `swap` function for user-defined types. (My question was initially motivated by a discussion of the illegality of adding types to namespace `std`.) I will not re-print the code snippet from the above-linked answer here. Instead, I would like to understand the answer. The answer I've linked above states, beneath the first code snippet, in regards to overloading `swap` in `namespace std` (rather than specializing it in that namespace): If your compiler prints out something different then it is not correctly implementing "two-phase lookup" for templates. The answer then goes on to point out that specializing `swap` in `namespace std` (as opposed to overloading it) produces a different result (the desired result in the case of specialization). However, the answer proceeds with an additional case: specializing swap for a user-defined template class - in which case, again, the desired result is not achieved. Unfortunately, the answer simply states the facts; it does not explain why. Can someone please elaborate on that answer, and describe the process of lookup in the two specific code snippets provided in that answer: overloading `swap` in `namespace std` for a user-defined non-template class (as in the first code snippet of the linked answer) specializing `swap` in `namespace std` for a user-defined template class (as in the final code snippet of the linked answer) In both cases, the generic `std::swap` is called, rather than the user-defined `swap`. Why? (This will shed light on the nature of two-phase lookup, and the reason for the best practice for implementing user-defined `swap`; thanks.)

Original source

Related problems