template <class T>
void Yeap(T);
int main() {
Yeap(0);
return 0;
}
template <class T>
void YeapImpl();
struct X;
template <class T>
void Yeap(T) {
YeapImpl<X>(); // pass X to another template
}
template <class T>
void YeapImpl() {
T().foo();
}
struct X {
void foo() {}
};
Note that struct X
is not defined until the very end. I used to believe that all odr-used names must be complete at the point of the instantiation. But here, how can the compiler treat it as a complete type prior to its definition?
I have checked the binding rules and lookup rules of dependent name and function template instantiation in cppreference, but none of them can explain what is happening here.
I believe this program is ill-formed, no diagnostic required.
[temp.point]/8 reads, editing out the irrelevant parts:
A specialization for a function template [...] may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.
YeapImpl<X>
has two points of instantiation: where it is called on the commented line in the question and at the end of the translation unit. In the first point of instantiation, X
is incomplete which would make the body of the function ill-formed. In the second point of instantiation, X
is complete which makes the body well-formed.
Those two specializations have [very] different meanings.