
AMDG On 02/25/2013 05:04 PM, Eric Niebler wrote:
You might be right. This is an interesting case. E.g. the following compiles with clang trunk:
template<typename T> struct identity { typedef T type; }; template<typename T> struct string_ref {};
template<typename T> void foo(string_ref<T>, string_ref<T>) {} <---- a
template<typename T> void foo(string_ref<T>, typename identity<string_ref<T> >::type) {} <---- b
template<typename T> void foo(typename identity<string_ref<T> >::type, string_ref<T>) {} <---- c
int main() { string_ref<int> ref; foo(ref, ref); }
Seems to rely on some partial ordering magic, even though all three are viable matches and they all yield the same function signature. At any rate, MSVC wasn't happy with it. It could be a compiler bug. Probably worth investigating and filing.
I don't think that this code is legal. (b) and (c) are obviously equivalent in terms of partial ordering. Neither is more specialized than the other. Unfortunately, I can't seem to find any provision for template paramters used in non-deduced contexts (as described in 14.8.2.5p5). 14.5.6.2 and 14.8.2.4 both appear to assume that this scenario never occurs. The transformation described in 14.5.6.2p3 is technically impossible to implement in this case, as the compiler cannot instantiate a template with the synthesized type. However, I can't come up with any sensible rule under which (a) is more specialized than (b). The simplest method (which I've always assumed to hold) is to treat template<typename T> void foo(string_ref<T>, typename identity<string_ref<T> >::type) {} as if it were template<typename T> void foo(string_ref<T>, typename identity<string_ref<char> >::type) {} in which case it is obvious that (b) should be considered more specialized than (a). Or, we could synthesize a unique type to substitute for identity<string_ref<char> >::type, in which case template argument deduction fails in both directions and neither (a) nor (b) is more specialized than the other. Or, we could treat it as if it were something like: template<typename T> void foo(string_ref<T>, identity___type<string_ref<T> >) {} (effectively transforming non-deduced contexts into deduced contexts) In this case, template argument deduction fails in both directions and neither (a) nor (b) is more specialized than the other. The only way I can see this working is if there's actually some kind of parameter-order dependence in the partial ordering rules that I missed, which makes (b) more specialized than (c). In Christ, Steven Watanabe