30
#include <iostream>

template<typename T>
struct identity
{
    typedef T type;
};

template<typename T> void bar(T) { std::cout << "a" << std::endl; }
template<typename T> void bar(typename identity<T>::type) { std::cout << "b" << std::endl; }

int main ()
{
    bar(5); // prints "a" because of template deduction rules
    bar<int>(5); // prints "b" because of ...?

    return EXIT_SUCCESS;
}

I expected bar<int>(5) to result in an ambiguity, at the very least. What crazy rule about template function overload resolution is involved here?

Barry
  • 286,269
  • 29
  • 621
  • 977
gd1
  • 11,300
  • 7
  • 49
  • 88
  • 6
    I would say that `typename identity::type` is more specialized than `T`. – Jarod42 Jul 13 '15 at 18:52
  • @Jarod42 `typename identity::type` is only a syntactically more verbose way of expressing`int`; it does not denote a more specialized kind of `int`. If we substitute `int` for the template parameter `T` in both functions, we get one which just takes `int`, and another which takes `typename identity::type`. **This should probably be ambiguous**. I mean, suppose you have a plain simple class A which contains a `typedef int B`. You don't give overload preference to a function which uses `A::B` over one that uses `int` in the same parameter position. – Kaz Jul 13 '15 at 21:19

1 Answers1

17

Once we get our candidate functions set (both bars), and then whittle it down to the viable functions (still both bars) we have to determine the best viable function. If there is more than one, we get an ambiguity error. The steps we take to determine the best one are laid out in [over.match.best]:

[A] viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

Both functions take an argument of type int, so both conversion sequences are identical. We continue.

— the context is an initialization by user-defined conversion [...]

Does not apply.

— the context is an initialization by conversion function for direct reference binding (13.3.1.6) of a reference to function type, [...]

Does not apply.

— F1 is not a function template specialization and F2 is a function template specialization, or, if not that,

Both bar<int>s are function template specializations. So we move onto the very last bullet point to to determine the best viable function.

— 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.

The partial ordering rules basically boil down to us synthesizing new unique types for the arguments of both bar overloads and performing template deduction on the other overload.

Consider the "b" overload first. Synthesize a type typename identity<Unique1>::type and attempt to perform template deduction against T. That succeeds. Simplest cast of template deduction there is.

Next, consider the "a" overload. Synthesize a type Unique2 and attempt to perform template deduction against typename identity<T>::type. This fails! This is a non-deduced context - no deduction can succeed.

Since template type deduction only succeeds in a single direction, the bar(typename identity<T>::type) overload is considered more specialized, and is chosen as the best viable candidate.


bogdan presents another interesting case for looking at partial ordering. Consider instead comparing:

template <typename T> void bar(T, T); // "c"
template <typename T> void bar(T, typename identity<T>::type ); // "d"

bar(5,5);
bar<int>(5, 5);

Again, both candidates are viable (this time even without explicitly specifying T) so we look at the partial ordering rule.

For the "c" overload, we synthesize arguments of type UniqueC, UniqueC and attempt to perform deduction against T, typename identity<T>::type. This succeeds (with T == UniqueC). So "c" is at least as specialized as "d".

For the "d" overload, we synthesize arguments of type UniqueD, typename identity<UniqueD>::type and attempt to perform deduction against T, T. This fails! The arguments are of different types! So "d" is not at least as specialized as "c".

Thus, the "c" overload is called.

Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    I think it's worth noting that the proposed resolution to [active issue 1391](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1391) will likely make this ambiguous. This whole area is poorly specified - just change the first declaration to `bar(T, T)` and the second one to `bar(T, typename identity::type)` and enjoy the surprise... – bogdan Jul 13 '15 at 20:48
  • @bogdan Yeah, worth adding as an example. It's an interesting one! – Barry Jul 13 '15 at 21:09
  • 1
    "For the "c" overload, [...]" - we can't expect the compiler to know what `identity::type` is for *any* invented `UniqueC`, so deduction can't actually succeed during partial ordering for this case. In particular, we can't generally tell that `identity::type` is always `T`, which is how we can make that deduction succeed. If we could, then we could do the same thing for the "d" overload, when we synthesize arguments of types `UniqueD, typename identity::type` - those would be equivalent to `UniqueD, UniqueD`, making deduction succeed the other way around as well. – bogdan Jul 13 '15 at 21:19
  • In short, following this line of reasoning, deduction either fails both ways or succeeds both ways. In either case, it doesn't yield a more specialized template. What actually happens, as far as I know, is that most compilers (Clang, GCC and EDG) treat `typename identity::type` as equivalent to another, unrelated, parameter `U`. This makes deduction succeed from "c" to "d" and fail the other way around, making "c" more specialized. MSVC, needless to say, thinks otherwise and reports an ambiguity (which is, funnily, more in line with the direction the standard seems to be taking). – bogdan Jul 13 '15 at 21:26
  • @bogdan It just says that we substitute in the synthesized type. It doesn't say that we actually instantiate any template that comes out of that substitution. – Barry Jul 13 '15 at 22:02
  • @bogdan [asked](http://stackoverflow.com/q/31394260/2069064) a question about that case. I think we do only perform instantiation on the deduction side, not the synthesis side. But maybe somebody else can chime in. – Barry Jul 13 '15 at 22:08