3

Consider the code:

#include <iostream>
#include <algorithm> // std::swap C++98
#include <utility> // std::swap C++11

namespace A
{
template<typename T>
struct Foo {};

template<typename T>
void swap(Foo<T> &lhs, Foo<T> &rhs)
{
    std::cout << "A::swap<T>" << std::endl;
}

} /* end namespace A */

namespace std // we explicitly specialize std::swap here
{

template<> // explicit template specialization for std::swap<int>
void swap(A::Foo<int> &lhs, A::Foo<int> &rhs) 
noexcept 
(is_nothrow_move_constructible<A::Foo<int>>::value && is_nothrow_move_assignable<A::Foo<int>>::value)
{
    std::cout << "std::swap<int>" << std::endl;
} 

} /* end namespace std */

int main()
{
    using std::swap;
    A::Foo<int> a, b; 
    A::Foo<double> x, y;

    swap(a, b); // ADL, expected to call std::swap<Foo<int>>, but NO
    swap(x, y); // ADL, expected to call A::swap<T>, YES
}

I would expect the std::swap explicit specialization to be a better candidate in the call swap(a, b), however it seems that the overload A::swap is always preferred, i.e. the output is:

A::swap<T>
A::swap<T>

Can anyone explain why this behaviour?

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • This excelent article by Herb Sutter might interest you: [Why Not Specialize Function Templates?](http://www.gotw.ca/publications/mill17.htm) – bolov Jan 22 '15 at 12:33

3 Answers3

8

Function template explicit specializations don't take part in overload resolution. Only function declarations synthesized from primary templates are considered. If one such function is chosen as the best viable function by the overload resolution process, an explicit specialization of the corresponding primary template will be used if suitable.

In this case, overload resolution needs to choose between a function declaration synthesized from your swap function template overload taking foo<T>& and one synthesized from the std::swap primary template taking T&.

None of these two functions can be chosen over the other based on conversions (they have the same function parameters), both are template specializations, so partial ordering of function templates is considered, which yields your swap function template as more specialized, so the function declaration synthesized from your swap overload wins.

bogdan
  • 9,229
  • 2
  • 33
  • 48
  • I agree the term *specialization* is confusing here. I don't think it is relevant if the function declaration "synthesized from the primary template" is used or the declaration of the explicit specialization (the declaration of the explicit spec cannot differ from the primary templ). As you say in the third paragraph, the types of the parameters of both functions in the overload set are equal; it doesn't care that they've been formed from different patterns (`foo&` vs `T&`) at this point. crucial: The final partial ordering relies on the *primary* templates, not explicit specializations. – dyp Jan 22 '15 at 19:09
  • @dyp I see what you mean - an interesting line of thought, and I think it's equally correct. I'd guess that the standard didn't follow this path to avoid the possibility of 'people getting any ideas' if explicit specializations were even mentioned in the description of the process. I usually try to follow the standard's logic as closely as possible when thinking about these things, to 'get into the mind of the compiler' (assuming *it* follows the standard...). – bogdan Jan 22 '15 at 20:25
  • @dyp The middle one, about the relevance of where the function declaration comes from. The last one is, of course, crucial, as you say, but it's called 'partial ordering of function *templates*' for a reason, after all - explicit specializations are no longer templates, so that part should be pretty clear. – bogdan Jan 22 '15 at 20:49
7

Explicit function template specializations never change which function template or overload is called, only the implementation of the template if it is called.

Overload resolution ignore specializations (as opposed to overloads, which can look a lot like partial specialization to someone unfamiliar with C++ function template quirks).

I can imagine why: mixing both overload and template specialization selection rules would make the rules even harder to follow and get right.

In general, it is rarely a good idea to specialize a function template: overloads, or dispatching to a template class, is usually better.

Note that the language talks about 'more specialized' in overload resolution: do not confuse this with 'template specialization': they are distinct concepts that unfortunetally share a word.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
2

As mentioned in other answers, the reason is that the signature of the explicit function specialization is not taking part in overload resolution.

A simple rule of thumb for explicit specializations is that they change the the definition that will be used when it's primary template is called with the specific template arguments.

template <typename T> void foo (T) {
  // definition used for 'foo' when T is not 'int'
}

template <> void foo<int> (int) {
  // definition used for 'foo' when T is 'int'
}

The compiler performs the following steps when selecting the best swap (I'll ignore exception specifications for brevity):

namespace A
{
  template <typename T> struct Foo { };

  template <typename T> void swap (Foo<T> &, Foo<T> &);
}

namespace std
{
  // swap provided by the STL
  template <typename T> void swap (T &, T &);

  // explicit specialization of std::swap
  template <> void swap (Foo<int> &, Foo<intT> &) { }
}

Specializations of the primary templates are generated for both swaps:

13.3.1p7: In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way.

NB: The explicit specialization is not part of this.

For swap(a,b) the candidate set used by overload resolution will contain the following generated function template specializations:

A::swap(Foo<int>&, Foo<int>)&);
std::swap(Foo<int>&, Foo<int>)&);

Both are generated template specializations and both have exactly the same conversion sequences for the arguments so to determine which template to use the following bullet in the Best Viable Function says:

13.3.3p1b7: F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.

Partial ordering comes down to comparing the function parameter lists to find which is more specialized. In this example, Foo<T> is more specialized than T and so swap(Foo<T>&, Foo<T>&) is considered a better match than swap(T&,T&). The result is that A::swap is selected and so the explicit specialization of std::swap is ignored.

If the code is changed so the explicit specialization is for A::swap rather than std::swap then as A::swap wins overload resolution, then the explicit specialization will be used:

namespace A
{
  template <typename T> struct Foo { };

  template <typename T> void swap (Foo<T> &, Foo<T> &);

  // explicit specialization of A::swap
  template <> void swap (Foo<int> &, Foo<intT> &) { }
}
Richard Corden
  • 21,389
  • 8
  • 58
  • 85
  • As part of my day job we put a rule set together for "Modern C++" and this is actually one of the rules (14.2.2). For those who are interested, my profile website links to the rules which are free to browse online. – Richard Corden Jan 22 '15 at 10:21