4

私はいくつかのテンプレートプログラミングで歯を食いしばっています、そして私はこれに非常に新しいです。実装したいのは、STLコンテナを含むいくつかのCRTPクラスです。CRTPスタイルに従ってコンパイル時にとが「派生」する(コンパイル時の)基本クラスのclass A{};例として使用してみましょう。class B{};class C{};

これで、との両方BCコンテナが含まれます。例の目的のために、それをそれぞれastd::vectorとaとしstd::setます。begin()ここで、これらのイテレータをaとend()、フォワードイテレータを公開する関数を介して公開したいと思います。ただし、内部にある正確なコンテナを公開したくないのでBCこれらの関数をに定義しAて、呼び出し時に正しいものをB使用できるようにしCます。

これは可能ですか?今のところ、私の計画は、のIterator内部クラスを持ち、BそれにC実際のイテレータ(場合によってはベクトルまたはセット)を含み、それに呼び出しを委任することです。ただし、これは複製されたグルーコードが多いようで、より良いオプションがあると思います。

いくつか質問があります。

  1. Aで内側の留め金を宣言して、 CRTPBでうまく機能するようにするにはどうすればよいですかCA、、のためにそれを複製する必要がBありCますか?で空のクラスにすることはできますか?また、特殊な実装でAそれらをマスクしますか?BC

  2. 接着剤と重複を減らしてイテレータを公開するにはどうすればよいですか?

boostのような外部ライブラリとの依存関係を作成したくないので、stdのみに固執したいと思います。ですから、自分で必要なものは何でも実装する必要があります。すべての助けをありがとう。

4

2 に答える 2

2

CRTP を介してイテレータも公開します。

template <typename T, typename Iter, typename ConstIter>
struct Base
{
    Iter begin() { return static_cast<T*>(this)->begin(); }
    Iter end() { return static_cast<T*>(this)->end(); }
    ConstIter begin() const { return static_cast<const T*>(this)->begin(); }
    ConstIter end() const { return static_cast<const T*>(this)->end(); }
};


struct B : Base<B, std::vector<int>::iterator, std::vector<int>::const_iterator>
{
    std::vector<int>::iterator begin() { return container.begin(); }
    ...

private:
    std::vector<int> container;
};

公開する型がさらにある場合は、特性クラスをテンプレート引数として に渡しますBase

template <typename T, typename Traits>
struct Base
{
    typename Traits::iterator begin() { ... }
    ...
};

// For this purpose, vector<int> makes a perfect traits class !
struct B : Base<B, std::vector<int> >
{
    std::vector<int>::iterator begin() { ... }
    ...
};

// Here is an example function taking Base as argument
template <typename T, typename Traits>
void foo(const Base<T, Traits>& x) 
{
    typename Traits::iterator i = x.begin();
    ...
}
于 2011-08-30T10:13:47.370 に答える
1

私があなたを正しく理解していれば、あなたはこのようなものを探しています。動作することを説明するためだけに、単純なコンストラクターをいくつか作成したことに注意してください。また、あなたclass Aは私のものですclass TWrapperBaseB- TWrapperBC- TWrapperC。別のこととして、この特定の例では 2 つの派生クラスを実際に持つ必要はありませんが、プログラムでそれを正当化するためにクラスが大幅に異なるBと想定しています。C

編集: ループで lIterSet をインクリメントするのを忘れました。

#include <vector>
#include <set>
#include <iostream>

template< typename PType, typename PContainer >
class TWrapperBase
{
 public:
  typedef PType TType;
  typedef PContainer TContainer;
  typedef typename TContainer::iterator TIterator;
 protected:
  TContainer mContainer;
 public:
  TWrapperBase( const TContainer& pOriginal ) :
   mContainer( pOriginal )
  {
  }
  TIterator begin( void )
  {
   return mContainer.begin();
  }
  TIterator end( void )
  {
   return mContainer.end();
  }
};

template< typename PType, class PContainer = std::vector< PType > >
class TWrapperB : public TWrapperBase< PType, PContainer >
{
 public:
  TWrapperB( const TContainer& pOriginal ) :
   TWrapperBase( pOriginal )
  {
  }
};

template< typename PType, class PContainer = std::set< PType > >
class TWrapperC : public TWrapperBase< PType, PContainer >
{
 public:
  TWrapperC( const TContainer& pOriginal ) :
   TWrapperBase( pOriginal )
  {
  }
};

int main( void )
{
 int lInit[] =
 {
 1, 2, 3
 };

 std::vector< int > lVec( lInit, lInit + 3 );
 std::set< int > lSet( lInit, lInit + 3 );

 TWrapperB< int > lB( lVec );
 TWrapperC< int > lC( lSet );

 std::vector< int >::iterator lIterVec = lB.begin();
 std::set< int >::iterator lIterSet = lC.begin();

 while( lIterVec < lB.end() )
 {
  std::cout << "vector: " << *lIterVec << " / set: " << *lIterSet << std::endl;
  lIterVec++;    
  lIterSet++;
 }

 return 0;
}
于 2011-08-30T10:05:32.920 に答える