Reputation: 5246
The following program was constructed to abuse some peculiarities of two-phase lookup in gcc/msvc. It compiles fine with both gcc/msvc and clang but results in different return value from function g
:
struct A;
struct C {};
struct D {
D (const A &);
};
struct B {
void f (const C&,int){x=0;};
void f (const D&,char){x=1;};
int x;
};
template<typename T>
int f(const A &y)
{
B x;
x.f(y,0); // Line 18
return x.x;
}
struct A
{
operator C () const;
};
int g (const A&x)
{
return f<int>(x);
}
https://gcc.godbolt.org/z/pqAVsU
Both GCC and MSVC call A::operator C
and return 0, while Clang calls D(const A &)
and returns 1.
Is it true according to the standard that clang is right and call on line 18 should be resolved while struct A
is not yet declared or is this the case of unspecified behavior?
Upvotes: 2
Views: 94
Reputation: 13040
The program is ill-formed, no diagnostic required, according to [temp.res]/8:
The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if:
...
the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the template. [ Note: This can happen in situations including the following:
a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is performed, or
...
...
In your program, y
is a non-dependent name and its type A
is incomplete in the definition of f
but complete when f<int>
is instantiated, so the rule above applies.
Upvotes: 2
Reputation: 63124
[temp.dep.candidate] says, and this might have to do with the compiler failures (notes and emphasis mine):
For a function call where the postfix-expression [that is the expression that designates the function, here
f
] is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:
- [Description of 2PL/ADL behaviour]
If the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.
However, x.f(y,0);
does not depend on T
at any point, so the scary paragraph above doesn't apply. We are in the case of a non-dependent lookup, covered by [temp.nondep] (note mine):
Non-dependent names used in a template definition are found using the usual name lookup and bound at the point they are used. [Example follows]
The whole expression should be resolved in the context of ::f
's definition, at which point A
is incomplete and its conversion operator is not visible. Clang is right and GCC and MSVC are both wrong.
Upvotes: 3