I have rather simple, I imagine, question about CRTP, but I cannot seem to find an answer to it. Probably, because it is so simple, no one thought of asking it. I am new to the concept, so, please don't laugh too hard ;).
Here is the code (it's sort of an attempt to do something similar to STL containers):
template< typename tT >
struct TBase
{
typedef tT T;
};
template< typename tTBase >
struct TTraitsBase
{
typedef typename tTBase::T T;
};
template< typename tTHelpee, typename tTTraits >
struct THelper
{
typedef typename tTTraits::T T;
typedef typename tTHelpee::T Th; /* This generates a compiler error:
'T' not being a member of TDerived<tT> */
T Foo( void )
{
return static_cast< tTHelpee* > ( this )->mVal;
}
};
template< typename tT >
struct TDerived : TBase< tT > , THelper< TDerived< tT > , TTraitsBase< TBase< tT > > >
{
using TBase< tT >::T;
T mVal;
};
int main()
{
TDerived< int >::T lTmp = -1;
TDerived< int > lObj;
lObj.mVal = -1;
std::cout << lObj.Foo() << std::endl;
return 0;
}
Everything compiles if I comment the offending typedef typename _THelpee::T Th;
. And that what confuses me: if compiler does not like typedef typename _THelpee::T Th;
, why does it let through static_cast< _THelpee* > ( this )->mVal
? I assume, it has something to do with not being able to instantiate THelper
when instantiating TDerived
, but no clear understanding. Can someone, please, give a brief explanation and/or some references on what is happening here? Thank you.
EDIT: removed '_T' prefix.
Implicitly instantiating a class does not instantiate its member function definitions. Each member of a class template is instantiated only when used (unless you use explicit instantiation).
The first thing you implicitly instantiate is TDerived<int>
, on the first line of main
. To instantiate that, the compiler looks up the TDerived
template, sees that there are a couple of dependent base classes, so tries to instantiate those. TBase<int>
instantiates with no problem. But on trying to instantiate THelper< TDerived<int>, TTraitsBase< TBase<int> > >
, there's an attempt to get a member TDerived<int>::T
, but TDerived<int>
is still an incomplete type.
Instantiating THelper< ... >
also notes that there is a member function int Foo()
, but does not evaluate the semantics of its definition.
By the time you call lObj.Foo()
from main
, which instantiates that int THelper< ... >::Foo()
, TDerived<int>
is a complete type, so there's no problem there.