5

I've noticed quite a few questions on templates that are due to the partial ordering rules and they all seem to be inconsistent with each other. I thought I'd dig into the standard myself and suss it out. Bear with me.

Both gcc and clang seem to agree on the algorithm for determining template ordering, but that algorithm doesn't actually appear in the standard. In no particular order:

Consistency of deduced values:

template <typename T> void foo(T, T); // (1)
template <typename T, typename U> void foo(T, U); // (2)

temp.deduct.type/2 makes it clear that there must be exactly one set of deduced values for the Ps. But there is no such statement in the partial ordering rule. The algorithm described only does pairwise P/A matching, so a synthesized call from (2) to (1) via foo(U{}, V{}) could succeed in deduction. Both gcc and clang agree that (1) is more specialized.

Type Synthesis Template Instantiation

template <typename T>
struct identity { using type = T; };

template<typename T> void bar(T, T ); // (1) 
template<typename T> void bar(T, typename identity<T>::type ); // (2)

Here, if synthesized for (2) Unique2 and typename identity<Unique2>::type == Unique2, then type deduction would succeed in both directions and the call bar(0,0) would be ambiguous. However, it seems that both compilers instead simply treat typename identity<Unique2>::type as Unique2_b, thus making template deduction from (2) to (1) fail (based on the implied missing Consistency rule).

Non-Deduced Context Omission

Same as previous example, but now define:

template <typename T> struct identity; template <> struct identity<int> { using type = int; };

With no template instantiation during synthesis and consistency, the (2) ==> (1) deduction fails. But if we consider the (1) ==> (2) call, we'd match T against Unique1 and then have the non-deduced context typename identity<Unique1>::type to match against Unique1, but that would be a substitution failure. It seems that the approach taken by gcc and clang (both of which prefer (1) here) is to ignore the non-deduced context argument, as long as that parameter type is deduced from a different template parameter type that did get matched.

Am I making any sense here?

The approaches that gcc/clang take make sense, but I don't think it's too clear in the standard. Can someone verify my logic?

  • I'm closing this as a dupe because bogdan's answer is pretty much what you're looking for. – Barry May 29 '16 at 01:19

0 Answers0