GCC accepts the following code:
template <typename T>
struct meta
{
typedef typename T::type type;
};
struct S {};
template <typename T>
typename meta<T>::type foo(T, S);
int foo(int, int);
int main()
{
foo(0, 0);
}
But clang rejects it with the following error:
test.cpp:4:22: error: type 'int' cannot be used prior to '::' because it has no members
typedef typename T::type type;
^
test.cpp:10:10: note: in instantiation of template class 'meta<int>' requested here
typename meta<T>::type foo(T, S);
^
test.cpp:10:24: note: while substituting deduced template arguments into function template 'foo' [with T = int]
typename meta<T>::type foo(T, S);
^
This seems to suggest a difference in the order in which GCC and clang do certain operations during overload resolution. GCC seems to throw out the template candidate because of the type mismatch in the second parameter (S
vs. int
) before trying to instantiate the return type of the template candidate, while clang seems to do it the other way around.
Who is right?
I believe this question has important implications for the authors of template libraries. Specifically, if clang is right, the author of the template foo
would have to do extra work to turn the error into a substitution failure.
EDIT: Note that the following slightly simpler example is rejected by both GCC and clang, with similar errors:
template <typename T>
struct meta
{
typedef typename T::type type;
};
template <typename T>
typename meta<T>::type foo(T);
int foo(int);
int main()
{
foo(0);
}
suggesting that GCC knows that "only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure". The difference between this example and the original is the presence of the second function parameter in the original example, on the basis of which GCC throws out the template candidate before it even gets to trying to perform substitution on the return type. I think the question is, whether GCC is correct to do things in that order, or should it be trying to perform substitution on the return type before considering matches in argument types.
UPDATE: Luc Danton's answer convinced me that clang is correct to reject the code. I have accordingly filed a GCC bug.