I\'m trying to understand if a simple CRTP pattern is valid by the standard.
The code below compiles and works as expected (on clang).
But my understanding o
The use of new Derived
causes the Derived
class to be instantiated.
Correction:
Derived
is not itself a template, so its structure layout and contained member declarations are needed right away. That causesCRTP<Derived,Base>
to be instantiated immediately following the Derived definition. I'll have to look up the formal standard later when I have more time; but the the point is still that the instantiation of CRTP only figures out the structure and available members, not the bodies of the member functions; and it knows the structure and members of the Derived class when it does so.The member functions are not instantiated until they are used (here, the constructor), and it already has the class itself at that time. The other thing to look up is whether the constructor of Derived, since it is not a template, is generated immediately following the class, or only if/when needed. If the former, it can be made lazy by making Derived a template with a dummy argument. But that doesn't affect this specific question: whether right after
Derived
or right aftermain
, the function instantiation is still not before parsing the declaration ofDerived
.
That causes CRTP<Derived,Base>
to be instantiated. But in both cases it is only the class structure that is needed, not the actual code for any of the members. Erase all the inline function bodies, and you'll see there is no problem at this point.
Now the Derived default constructor is used, so Derived::Derived()
is implicitly instantiated. The point of instantiation is immediately following the definition of main
.
In instantiating Derived::Derived()
, it then needs CRTP<Derived,Base>::CRTP()
. It is instantiated at the same point as the template instantiation that needed it. That constructor needs all the virtual functions, so DoSomething()
is instantiated, again, at the same point as the instantiation that kicked it off. You can see that all this happens well after the complete definition of the fully rendered Derived class is known, in terms of all the declarations of all the members (not the function bodies).
That's the missing insight: the class definition does not include member function definitions, even if they are given within the lexical enclosing region of the class definition. Remember the distinction between definitions and declarations, separately for classes and functions.
This appears to be a result of the compiler delaying instantiation of CRTP<Derived, Base>::DoSomething()
until the end of the translation unit, as it is allowed to do (see CWG issue 993).
CRTP<Derived, Base>
is definitely instantiated right before the definition of Derived
(§14.6.4.1 [temp.point]/p4, all quotes are to N3936):
For a class template specialization, a class member template specialization, or a specialization for a class member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
Whether CRTP<Derived, Base>::DoSomething()
is required to be instantiated at all depends on the meaning of the phrase referenced in a context that requires the member definition to exist (§14.7.1 [temp.inst]/p2). All non-pure virtual functions are odr-used (§3.2 [basic.def.odr]/p2), and "every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program" (§3.2 [basic.def.odr]/p4); whether that counts as "referenced in a context that requires the member definition to exist" is unclear.
(Even if it's not required to be instantiated, however, the compiler is still free to instantiate it per §14.7.1 [temp.inst]/p11 - "It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated.".)
If CRTP<Derived, Base>::DoSomething()
is indeed instantiated, then the situation is covered by §14.6.4.1 [temp.point]/p5 and p8 (emphasis mine):
5 If a virtual function is implicitly instantiated, its point of instantiation is immediately following the point of instantiation of its enclosing class template specialization.
8 A specialization for a function template, a member function template, or of a member function or static data member of a class 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. A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.
That is, it has two points of instantiation, one right after CRTP< Derived, Base >
's point of instantiation, and one at the end of the translation unit. In this case, at the two points of instantiations name lookup for typename T::Type
would produce different results, so the program is ill-formed, no diagnostic required.