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 of the relevant standard chapters/paragraphs is that the point of instantiation of the virtual function CRTP< Derived, Base >::DoSomething() should be at point (B) of the code, where the full declaration of Derived is not available. Therefore the inner typedef Type should not be available either.
Can anyone kindly point out the relevant standard chapter that validates this code?
In other words, something that says that in this case the virtual function is instantiated ATFER point C? Thanks a lot in advance for any insight.
Francesco
//-------------------------
// START CODE
#include <iostream>
struct Type1 {};
struct Type2 {};
struct Base
{
virtual ~Base() {}
virtual void DoSomething() = 0;
};
template< typename T, typename U >
struct CRTP : U
{
virtual void DoSomething() { DoSomething( typename T::Type() ); }
void DoSomething( Type1 ) { std::cout << "1\n"; }
void DoSomething( Type2 ) { std::cout << "2\n"; }
};
// (A) point of inst. of CRTP< Derived, Base > ( 14.7.1.4 ) ??
// (B) point of inst. of CRTP< Derived, Base >::DoSomething() (14.6.4.1.4 ) ??
struct Derived : CRTP< Derived, Base >
{
typedef Type2 Type;
};
// (C)
int main()
{
Base * ptr = new Derived;
ptr->DoSomething();
delete ptr;
}
// END CODE
//-------------------------
Relevant (?) standard paragraphs:
14.6.4.1 4 If a virtual function is implicitly instantiated, its point of instantiation is immediately following the point of instantiation of its enclosing class template specialization.
14.7.1 4 A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type might affect the semantics of the program.
14.7.1 9 An implementation shall not implicitly instantiate a function template, a member template, a non-virtual member function, a member class, or a static data member of a class template that does not require instan- tiation. 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With