c++typedefcrtp

CRTP-related compiler error on typedef


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.


Solution

  • 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.