I am experiencing an overload resolution behaviour that seems very unexpected. The following code is rejected with an ambiguity error by both gcc and clang:
template <typename T>
struct A
{
typedef T key_type;
};
template <typename T>
void foo(A<T> rng, T val);
template <typename T, typename U = T>
void foo(T, typename U::key_type);
int main()
{
A<int> i;
foo(i, 0);
}
The error is:
test.cpp:16:5: error: call to 'foo' is ambiguous
foo(i, 0);
^~~
test.cpp:8:6: note: candidate function [with T = int]
void foo(A<T> rng, T val);
^
test.cpp:11:6: note: candidate function [with T = A<int>, U = A<int>]
void foo(T, typename U::key_type);
^
I would expect both to be exact matches but the first overload to win in partial ordering, since in the first parameter A<T>
is more specialized than T
.
What blows my mind is that if I change the second signature to:
template <typename T, typename U = T>
void foo(T, typename T::key_type);
both gcc and clang now accept the code, and choose the first overload as I would originally expect.
I don't see how this change can make a difference in behaviour: all I did was substitute the use of a template parameter which was neither explicitly specified nor deduced (U
) with its default value (T
).
Then again, the behaviour before the change is unexpected to begin with, so maybe I am missing something.
Could someone explain:
- why the first case is ambiguous; and
- why the change I made resolves the ambiguity?
In case it's relevant, the compiler versions I tested with are gcc 4.8.0 and a recent trunk build of clang.