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.