21

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.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
HighCommander4
  • 50,428
  • 24
  • 122
  • 194

2 Answers2

11

Both Clang and g++ are correct here.

Per Luc Danton's answer, a compiler is permitted to deduce T = int for the foo function template. Then, during the substitution of that value into the declaration of foo, the implicit instantiation of meta<int> is required, and it results in an error outside the immediate context of the substitution (so SFINAE does not apply). So Clang is correct to reject this code.

However, [temp.inst]p7 says:

If the overload resolution process can determine the correct function to call without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.

Because the non-template foo is an exact match for the arguments in the call, a compiler could determine that a function template specialization will never be the best viable function, and so need not perform argument deduction and substitution. Therefore g++ is correct to not reject this code.

Richard Smith
  • 13,696
  • 56
  • 78
  • Hmmm. It's not clear if this skipping of class template instantiation can bypass determining the type of the function template specialization, or just skip unrelated class template members. – aschepler Jul 07 '13 at 07:01
  • @aschepler The standard seems clear here: it's permitted for any class template instantiation triggered during overload resolution. – Richard Smith Jul 07 '13 at 07:06
  • Very interesting! Do you think it's a good idea that the standard leaves this unspecified? It seems that this creates a rather large set of programs that compile under one standard-conforming implementation and don't under another. – HighCommander4 Jul 07 '13 at 17:28
5

C++03 uses this wording as part of the specification for what is usually referred to as SFINAE (14.8.2 Template argument deduction [temp.deduct], paragraph 2):

[...] If a substitution in a template parameter or in the function type of the function template results in an invalid type, type deduction fails. [...]

By contrast, C++11 uses this wording (14.8.2 Template argument deduction [temp.deduct], paragraph 8):

If a substitution results in an invalid type or expression, type deduction fails. [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [...]

Emphasis is mine. As I understand it the wording was improved in C++11 to unambiguously outline what should result in SFINAE (so-called soft errors) and what shouldn't (hard errors). This 2008 paper is an example of the discussion that was going on at the time and led to the current rules.

With this in mind it may be the case that according to C++03 an implementation may be right to accept your code (and even perhaps it should). I suspect that a C++11 implementation should reject it however: the error (int::type) is in the context of meta<int>, not of foo<int>.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • Please see my edit to the question. I believe the reason GCC accepts the code is *not* because it is following the C++03 rules (as demonstrated by the example in the edit), but that it is throwing out the template candidate because of the mismatch in the second parameter type, before it gets to performing substitution on the return type. – HighCommander4 Aug 18 '12 at 05:38
  • I'm wondering why an implementation would attempt to instantiate the template, when there exists an "exact" match for the function call. If that is what an implementation should do, then does it mean that `template T foo(T, T)` would be called (had there been such a template), instead of `int foo(int,int)` for the function call `foo(0,0)`? – Nawaz Aug 18 '12 at 05:49
  • 3
    @HighCommander4 From 13.3.1 Candidate functions and argument lists [over.match.funcs], paragraph 7: "In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way. A given name can refer to one or more function templates and also to a set of overloaded non-template functions. In such a case, the candidate functions generated from each function template are combined with the set of non-template candidate functions." – Luc Danton Aug 18 '12 at 05:55
  • Reference is for both C++03 and C++11. – Luc Danton Aug 18 '12 at 05:56
  • @LucDanton: I think that answers Nawaz's question, but not mine. – HighCommander4 Aug 18 '12 at 06:09
  • 1
    @HighCommander4 "Those candidates are **then** handled as candidates function" suggests there is an ordering. – Luc Danton Aug 18 '12 at 06:31
  • @LucDanton: Ah, I see now. Thanks! I will file a GCC bug then. – HighCommander4 Aug 18 '12 at 06:36
  • @LucDanton: Do you think *that* quote (in your comment) answers my question? I don't see how. – Nawaz Aug 18 '12 at 07:04
  • 2
    @Nawaz Those rules govern overload resolution. Every candidate is considered (and in the case that function templates are involved, relevant specializations are instantiated, which may lead to SFINAE or yield hard errors) *before* a specific function is picked. – Luc Danton Aug 18 '12 at 07:09