开发者

CRTP'ed Containers

开发者 https://www.devze.com 2023-04-01 11:20 出处:网络
I am cutting my teeth at som开发者_Go百科e template programming and I am very new to this. What I want to implement are a few CRTP classes that contain an STL container. Let class A{}; serve as an exa

I am cutting my teeth at som开发者_Go百科e template programming and I am very new to this. What I want to implement are a few CRTP classes that contain an STL container. Let class A{}; serve as an example for the (compile time) base class from which class B{}; and class C{}; are "derived" at compile time following the CRTP style.

Now both B and C will contain containers. For the purpose of the example let it be a std::vector and a std::set respectively. Now, I want to expose the iterators of these via a begin() and an end() function that exposes a forward iterator. However, I do not want to expose what is the exact container that is inside B and C and I want to define these functions for A, so that at call time the correct one for B and C get used.

Is this possible ? Right now my plan is to have a Iterator inner class for B as well as C that will contain the actual iterator of (a vector or a set as the case may be) and delegate the call to it. However this seems to be a lot of replicated glue code and I suspect there is a better option.

I have a couple of questions:

  1. How do I declare the inner clases in A, B and C so that it plays well with CRTP. Do I need to replicate it for A, B and C ? Can it be an empty class in A and I mask them in B and C with specialized implementations ?

  2. How can I expose the iterator with less glue and less duplication ?

I do not want to create dependencies with external libraries like boost and want to stick to std only. So I have to implement whatever extra I need myself. Thanks for all the help.


Expose the iterator too via 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;
};

If you have more types to expose, then pass a traits class as a template argument to 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();
    ...
}


If I understood you corretcly, you are looking for something like this. Note, I made some simple constructor just to illustrate that it works. Also, your class A is mine class TWrapperBase, B - TWrapperB, C - TWrapperC. Another thing, you don't really need to have two derived classes for this particular example, but I assume your classes B and C are significantly different to justify it in your program.

EDIT: Forgot to increment lIterSet in the loop.

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

精彩评论

暂无评论...
验证码 换一张
取 消