TL;DR; This is ill-formed according to the standard, which is why you get warnings/errors when compiling with gcc/clang. There is an ambiguity between the first and the third call because the implicit conversion sequence from unsigned char to short is worse than the one from unsigned char to int (first one is a conversion while the second one is a promotion).
You have to consider 3 different implicit conversion sequences in this example, each one containing a single standard conversion:
- unsigned charto- unsigned char, which is an exact match.
- unsigned charto- shortwhich is an integral conversion.
- unsigned charto- intwhich is an integral promotion.
The ranking of the conversion sequences is a follows:
exact match > integral promotion > integral conversion
For an overload FooA to be better than an overload FooB, implicit conversions for all arguments of FooA have to be not worse than implicit conversions for arguments of FooB, and at least one of the implicit conversion for FooA must be better than the corresponding conversion for FooB.
In your case:
- Foo(int, int)vs.- Foo(short, short)— First one is better because- unsigned charto- int(promotion) is better than- unsigned charto- short(conversion).
- Foo(int, int)vs.- Foo(short, unsigned char)— This is ambiguous:- 
- unsigned charto- intis better than- unsigned charto- short.
- unsigned charto- intis worse than- unsigned charto- unsigned char.
 
- Foo(short, short)vs.- Foo(short, unsigned char)— Second one is better because- unsigned charto- unsigned char(exact match) is better than- unsigned charto- short(conversion).
There is an ambiguity only between Foo(int, int) and Foo(short, unsigned char), the second overload Foo(short, short) does not participate in the "ambiguity". If you remove either the first or the third overload, the ambiguity disappears and the first or the third overload is chosen.