Контейнеры CRTP - PullRequest
       10

Контейнеры CRTP

4 голосов
/ 30 августа 2011

Я режу зубы при программировании некоторых шаблонов, и я очень новичок в этом.Я хочу реализовать несколько классов CRTP, которые содержат контейнер STL.Пусть class A{}; послужит примером для базового класса (времени компиляции), из которого class B{}; и class C{}; "получены" во время компиляции, следуя стилю CRTP.

Теперь оба B и C будет содержать контейнеры.Для целей примера пусть это будут std::vector и std::set соответственно.Теперь я хочу представить их итераторы через begin() и функцию end(), которая предоставляет прямой итератор.Однако я не хочу раскрывать, какой именно контейнер находится внутри B и C, и я хочу определить эти функции для A, чтобы во время вызова правильный для B и C привыкнуть.

Возможно ли это?Прямо сейчас мой план состоит в том, чтобы иметь Iterator внутренний класс для B, а также C, который будет содержать фактический итератор (вектор или набор в зависимости от обстоятельств) и делегировать ему вызов.Однако, похоже, что это много реплицированного клея-кода, и я подозреваю, что есть лучший вариант.

У меня есть пара вопросов:

  1. Как мне объявитьвнутренние выражения в A, B и C, так что они хорошо работают с CRTP.Нужно ли копировать его для A, B и C?Может ли это быть пустой класс в A, и я маскирую их в B и C со специализированными реализациями?

  2. Как я могу представить итератор с меньшим количеством клея и меньшим дублированием?

Я не хочу создавать зависимости с внешними библиотеками, такими как boost, и хочу придерживаться только std.Поэтому я должен реализовать все, что мне нужно, самому.Спасибо за помощь.

Ответы [ 2 ]

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

Выставить итератор тоже через 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();
    ...
}
1 голос
/ 30 августа 2011

Если я вас правильно понял, вы ищете что-то вроде этого. Обратите внимание, я сделал простой конструктор, чтобы проиллюстрировать, что он работает. Кроме того, ваш class A мой class TWrapperBase, B - TWrapperB, C - TWrapperC. Другое дело, что вам не нужно иметь два производных класса для этого конкретного примера, но я предполагаю, что ваши классы 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;
}
...