Контейнеры CRTP

Я режу зубы в некотором программировании шаблона, и я очень плохо знаком с этим. Я хочу реализовать несколько классов 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 ответа

Решение

Если я вас правильно понял, вы ищете что-то вроде этого. Обратите внимание, я сделал простой конструктор, чтобы проиллюстрировать, что он работает. Кроме того, ваш 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;
}

Выставьте итератор тоже через 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();
    ...
}
Другие вопросы по тегам