CRTP-связанная ошибка компилятора на typedef - PullRequest
0 голосов
/ 29 августа 2011

У меня довольно простой вопрос, я думаю, о CRTP , но я не могу найти ответ на него.Вероятно, потому что это так просто, никто не думал об этом спрашивать.Я новичок в этой концепции, поэтому, пожалуйста, не смейтесь слишком сильно;).

Вот код (это своего рода попытка сделать что-то похожее на контейнеры STL):

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;
}

Все компилируется, если я комментирую оскорбление typedef typename _THelpee::T Th;.И это меня смущает: если компилятору не нравится typedef typename _THelpee::T Th;, почему он пропускает static_cast< _THelpee* > ( this )->mVal?Я предполагаю, что это как-то связано с неспособностью создать экземпляр THelper при создании экземпляра TDerived, но нет ясного понимания.Может ли кто-нибудь дать краткое объяснение и / или некоторые ссылки на то, что здесь происходит?Спасибо.

РЕДАКТИРОВАТЬ: удален префикс _T.

Ответы [ 3 ]

2 голосов
/ 29 августа 2011

Неявное создание экземпляра класса не создает его определения функций-членов. Каждый член шаблона класса создается только при использовании (если вы не используете явную реализацию).

Первое, что вы неявно создаете, - это TDerived<int> в первой строке main. Чтобы создать это, компилятор ищет шаблон TDerived, видит, что есть пара зависимых базовых классов, поэтому пытается их создать. TBase<int> создает без проблем. Но при попытке создать экземпляр THelper< TDerived<int>, TTraitsBase< TBase<int> > >, есть попытка получить член TDerived<int>::T, но TDerived<int> все еще не завершен.

Instantiating THelper< ... > также отмечает, что есть функция-член int Foo(), но не оценивает семантику ее определения.

К тому времени, когда вы звоните lObj.Foo() из main, что говорит о том, что int THelper< ... >::Foo(), TDerived<int> является полным типом, так что проблем здесь нет.

2 голосов
/ 29 августа 2011

Есть пара проблем с размещенным кодом.Поскольку я не мог разобрать намерение, я перечислю то, что я нашел ошибочным.

(1) Использование неполного типа :

template< typename _T >
struct TDerived : TBase< _T > , THelper< TDerived< _T > , TTraitsBase< TBase< _T > > >
                                         ^^^^^^^^^^^^^^ it's incomplete

IMO, когда вы определяете тело TDerived<_T>, вы не должны использовать его как параметр для чего-либо.

(2) Неправильный тип определение.

using TBase< _T >::T;

должно быть,

typedef typename TBase< _T >::T T;
1 голос
/ 29 августа 2011

Я бы сказал, что у вас есть циклическая зависимость в вашем CRTP (как это было бы по самой своей природе), но это означает, что базовому классу шаблона не разрешено использовать любой из егокласс параметров, потому что этот класс параметров еще не определен - он все еще неполный тип в момент создания экземпляра Base<T>.

Итак, все в порядке:

template <typename T> struct Base
{
  T * impl;  // incomplete type is OK
};

class Foo : public Base<Foo> { /* ... */ };

Но это не так:

template <typename T> struct Base
{
  T x; // need to know T, but T depends on Base<T>!
};
...