CRTP-связанная ошибка компилятора в значении по умолчанию для указателя на функцию-члена - PullRequest
0 голосов
/ 19 ноября 2011

Привет,

При создании универсальной обертки на основе CRTP для вызова произвольных библиотечных функций я столкнулся с проблемой, которую мне трудно понять.Вот очень упрощенный код для иллюстрации проблемы:

#include <iostream>

template< typename PValue, typename PDerived >
class TBase
{
 private:
  typedef TBase TSelf_;
  typedef PDerived TDerived_;

 protected:
  typedef PValue TValue_;

 protected:
  TBase( void )
  {
   std::cout << " TBase::TBase() " << std::endl;
  }

 public:
  void Foo( void )
  {
   std::cout << " TBase::Foo() " << std::endl;
  }

  template< typename PType >
  static void Call( PType /*pSomething*/, void(TDerived_::*pFunction)( void ) = &TSelf_::Foo, TDerived_ pDerived = TDerived_() )
  {
   ( pDerived.*pFunction )();
   std::cout << " static TBase::Call(). " << std::endl;
  }
};

template< typename PValue >
class TDerived : public TBase< PValue, TDerived< PValue > >
{
  friend class TBase< PValue, TDerived< PValue > > ;
 private:
  typedef TBase< PValue, TDerived > TBase_;
  typedef TDerived TSelf_;
 public:
  TDerived( void ) :
   TBase_()
  {
   std::cout << " TDerived::TDerived() " << std::endl;
  }
  void Foo( void )
  {
   std::cout << " TDerived::Foo() " << std::endl;
  }
  void Bar( void )
  {
   std::cout << " TDerived::Bar() " << std::endl;
  }
};

int main( void )
{
 TDerived< int >::Call( 1 );
 TDerived< int >::Call( 1, &TDerived< int >::Foo );
 TDerived< int >::Call( 1, &TDerived< int >::Bar, TDerived< int > () );
 return ( 0 );
}

Все компилируется и работает, как задумано.Однако, если я пытаюсь использовать указатель на TDerived::Foo() в качестве аргумента по умолчанию для второго параметра в компиляторах TBase::Call(...):

static void Call( PType /*pSomething*/, void(TDerived_::*pFunction)( void ) = &TDerived_::Foo, TDerived_ pDerived = TDerived_() )

дает синтаксическую ошибку ... У меня такое ощущение, что это связано скак компилятор анализирует код и что он не может определить указатель на функцию еще не определенного (или созданного) класса.Однако у него нет проблем с вызовом конструктора TDerived в качестве аргумента по умолчанию для третьего параметра TBase::Call(...).Может ли кто-нибудь дать мне определенный ответ о том, что происходит?Почему MFP производного класса не принимается, а объект производного класса принимается в качестве аргументов по умолчанию?

Спасибо.

EDIT: ошибка компилятора (компилятор командной строки MSVS2010):

FMain.cpp(224) : error C2061: syntax error : identifier 'TDerived_'; FMain.cpp(233) : see reference to class template instantiation 'TBase<PValue,PDerived> with [PValue=int,PDerived=TDerived<int>]' being compiled; FMain.cpp(323) : see reference to class template instantiation 'TDerived<PValue> with [PValue=int]' being compiled

Это синтаксическая ошибка - он не распознает TDerived_ как тип в качестве аргумента по умолчанию для MFP.После этой ошибки есть и другие ошибки, все они являются синтаксическими ошибками, поскольку определение функции в настоящее время некорректно.Вот как я это понимаю.

РЕДАКТИРОВАТЬ: По сути, я не понимаю, почему я могу использовать объект TDerived_ в качестве аргумента по умолчанию, но не могу использовать указатель на функцию-член по умолчаниюаргумент.

РЕДАКТИРОВАТЬ: Хорошо, это сводит меня с ума сейчас.Прежде всего, я изменил на typedef TBase< PValue, TDerived > TBase_;, как было указано (спасибо, ребята!).На самом деле, он компилируется только под MSVC ++, поскольку этот компилятор не выполняет двухэтапный анализ;то есть на codepad.org (который использует g ++ 4.1.2) он не компилировался.Во-вторых, после этого я попытался использовать static void Call( PType /*pSomething*/, void(TDerived_::*pFunction)( void ) = &TDerived_::Foo, TDerived_ pDerived = TDerived_() ) на codepad.org и ... он скомпилирован и работает правильно!Так что я ДЕЙСТВИТЕЛЬНО растерялся: люди объяснили мне, почему это не правильно (и я не мог понять «почему» (см. Мой предыдущий РЕДАКТИРОВАТЬ)), и теперь оказывается, что g ++ компилирует это правильно ... Это означает, что это простоПроблема MSVC ++ а не код?Или код имеет проблему со стандартной точки зрения (а я ее не вижу) и g ++ принимает ее «по ошибке» (я думаю, вряд ли)? .. Помогите?!

Ответы [ 2 ]

2 голосов
/ 19 ноября 2011

Область видимости параметра TValue_ для типа в typedef для TBase_ в TDerived представляется неправильной (!)

У вас есть:

 private:
  typedef TBase< TValue_, TDerived > TBase_;

Я думаю, вам нужно:

 private:
  typedef TBase< typename TBase< PValue, TDerived< PValue > >::TValue_, TDerived > TBase_;

Или даже просто:

 private:
  typedef TBase< PValue, TDerived > TBase_;

РЕДАКТИРОВАТЬ : стандартная часть C ++ 14.6.2, параграф 3, охватывает этот случай:

В определении класса или шаблона класса, если базовый класс зависит от параметра-шаблона, область действия базового класса не проверяется при поиске безусловного имени ни в точке определенияшаблон класса или член или во время создания шаблона класса или члена

1 голос
/ 19 ноября 2011

Простой: при создании экземпляра TBase< int, TDerived< int> > шаблона определения класса создается объявление шаблона функции Call<>:

  template< typename PType >
  static void Call( PType , void(TDerived_::*pFunction)() = &TSelf_::Foo, TDerived_ pDerived = TDerived_() )

TDerived_ = TDerived< int>), что хорошо, так как TSelf_::Foo() объявлен на этом этапе.

OTOH, проблема с

static void Call( PType , void(TDerived_::*pFunction)() = &TDerived_::Foo, TDerived_ pDerived = TDerived_() )

заключается в том, что TDerived_::Foo() не объявляется во время TBase< int, TDerived< int> > создания определения класса шаблона.

Кстати, вам не нужно указывать список параметров как ( void );() имеет тот же эффект и менее многословен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...