9

Consider this example:

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.

Barry
  • 247,587
  • 26
  • 487
  • 819
felix
  • 1,919
  • 5
  • 15
  • @JeJo Yes, with only a forward declaration of X, we can use X as an incomplete type, such as defining a pointer to it and use it as the return type of a function declaration(but not in a function definition). But here, I successfully create an instance of it and call a member function on it. – felix Sep 18 '18 at 14:55
  • Possible duplicate of [Pimpl - Why can make\_unique be called on an incomplete type](https://stackoverflow.com/questions/52180744/pimpl-why-can-make-unique-be-called-on-an-incomplete-type) – Geezer Sep 18 '18 at 17:59
  • According to [\[temp.inst\]/8](https://timsong-cpp.github.io/cppwp/n4659/temp.inst#8) *If a function template or a member function template specialization is used in a way that involves overload resolution, a declaration of the specialization is implicitly instantiated*. Then in the current draft of the standard [\[expr.call\]/3](https://timsong-cpp.github.io/cppwp/expr.call#3), *If a function or member function name is used, the appropriate function and the validity of the call are determined according to the rules in [over.match].* – Oliv Sep 19 '18 at 06:32
  • So at the point of instantion of Yeap, after main, only the declaration of Yeap is instantiated, not its definition. The definition of `Yeap` is instantiated at the end of the TU. There are no issue with dependent name resolution. – Oliv Sep 19 '18 at 06:34

1 Answers1

8

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.

Barry
  • 247,587
  • 26
  • 487
  • 819
  • 2
    I always forget that an instantiation is a (non-explicit) specialization. I should tattoo my forearm with [`[temp.spec]/4`](http://eel.is/c++draft/temp.spec#4.sentence-2)... – YSC Sep 18 '18 at 15:22
  • @YSC It's an unfortunate overloading of the term... it confuses me periodically. – Barry Sep 18 '18 at 15:49
  • We might get a better deal for identical tattoos. – YSC Sep 18 '18 at 15:50
  • Are the instantiation of the definitions of these function required until the end of the program? There are no return type do deduce, so instantiation of the declaration could be sufficient. – Oliv Sep 18 '18 at 19:41
  • I have just posted a question about [that](https://stackoverflow.com/q/52394125/5632316), thank! :) – Oliv Sep 18 '18 at 20:27