0

やあ、

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()の2番目のパラメーターのデフォルト引数としてポインターを使用しようとすると、TBase::Call(...)

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

コンパイラーは構文エラーを出します...コンパイラーがコードを解析する方法に関連していて、まだ定義されていない(またはインスタンス化されていない)クラスの関数へのポインターを理解できないと感じています。ただし、TDerivedの3番目のパラメーターのデフォルト引数としてコンストラクターを呼び出すことは問題ありませんTBase::Call(...)。誰かが私に何が起こっているのかについて明確な答えを与えることができますか?派生クラスMFPが受け入れられず、派生クラスのオブジェクトがデフォルトの引数として受け入れられるのはなぜですか?

ありがとう。

編集:コンパイラのエラー(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_;指摘された通りに変更しました(ありがとうございます!)。実際、このコンパイラは2つの部分からなる解析を行わないため、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 ++はそれを「誤って」受け入れます(おそらく、私は思いません)?..ヘルプ?!

4

2 に答える 2

2

TValue_typedef for TBase_inの型へのパラメーターのスコープ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は、このケースをカバーしています:

クラスまたはクラステンプレートの定義で、基本クラスがtemplate-parameterに依存している場合、基本クラススコープは、クラステンプレートまたはメンバーの定義の時点での非修飾名ルックアップ中、またはクラステンプレートまたはメンバー

于 2011-11-19T05:55:49.923 に答える
1

シンプル:TBase< int, TDerived< int> >テンプレートクラス定義Call<>をインスタンス化すると、関数テンプレートの宣言がインスタンス化されます。

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

(with 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 );として指定する必要はありません。()同じ効果があり、冗長性が低くなります。

于 2011-11-19T07:32:43.843 に答える