Контейнеры 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
он будет содержать фактический итератор (вектор или набор в зависимости от обстоятельств) и делегировать ему вызов. Однако, похоже, это много реплицированного кода, и я подозреваю, что есть лучший вариант.
У меня есть пара вопросов:
Как мне объявить внутренние предложения в
A
,B
а такжеC
так что хорошо играет с CRTP. Нужно ли копировать это дляA
,B
а такжеC
? Может ли это быть пустой класс вA
и я маскирую ихB
а такжеC
со специализированными реализациями?Как я могу показать итератору меньше клея и меньше дублирования?
Я не хочу создавать зависимости с внешними библиотеками, такими как 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();
...
}