Как я могу предоставить итераторы, не раскрывая используемый контейнер?
-
03-07-2019 - |
Вопрос
Я уже некоторое время использую C#, и возвращение к C++ — головная боль.Я пытаюсь перенести часть своих практик с C# на C++, но встречаю некоторое сопротивление и буду рад принять вашу помощь.
Я хотел бы предоставить итератор для такого класса:
template <class T>
class MyContainer
{
public:
// Here is the problem:
// typedef for MyIterator without exposing std::vector publicly?
MyIterator Begin() { return mHiddenContainerImpl.begin(); }
MyIterator End() { return mHiddenContainerImpl.end(); }
private:
std::vector<T> mHiddenContainerImpl;
};
Пытаюсь ли я сделать что-то, что не является проблемой?Должен ли я просто ввести def std::vector< T >::iterator?Я надеюсь просто зависеть от итератора, а не от реализующего контейнера...
Решение
Следующая статья может показаться вам интересной, поскольку она посвящена именно той проблеме, о которой вы сообщили: О разногласиях между объектно-ориентированным и обобщенным программированием на C++ и о том, как с этим может справиться Type Erasure
Другие советы
Раньше я делал следующее, чтобы получить итератор, независимый от контейнера.Возможно, это было бы излишним, поскольку я мог бы также использовать API, через который вызывающий объект передает vector<T*>&
он должен быть заполнен всеми элементами, а затем вызывающий объект может просто выполнить итерацию напрямую из вектора.
template <class T>
class IterImpl
{
public:
virtual T* next() = 0;
};
template <class T>
class Iter
{
public:
Iter( IterImpl<T>* pImpl ):mpImpl(pImpl) {};
Iter( Iter<T>& rIter ):mpImpl(pImpl)
{
rIter.mpImpl = 0; // take ownership
}
~Iter() {
delete mpImpl; // does nothing if it is 0
}
T* next() {
return mpImpl->next();
}
private:
IterImpl<T>* mpImpl;
};
template <class C, class T>
class IterImplStl : public IterImpl<T>
{
public:
IterImplStl( C& rC )
:mrC( rC ),
curr( rC.begin() )
{}
virtual T* next()
{
if ( curr == mrC.end() ) return 0;
typename T* pResult = &*curr;
++curr;
return pResult;
}
private:
C& mrC;
typename C::iterator curr;
};
class Widget;
// in the base clase we do not need to include widget
class TestBase
{
public:
virtual Iter<Widget> getIter() = 0;
};
#include <vector>
class Widget
{
public:
int px;
int py;
};
class Test : public TestBase
{
public:
typedef std::vector<Widget> WidgetVec;
virtual Iter<Widget> getIter() {
return Iter<Widget>( new IterImplStl<WidgetVec, Widget>( mVec ) );
}
void add( int px, int py )
{
mVec.push_back( Widget() );
mVec.back().px = px;
mVec.back().py = py;
}
private:
WidgetVec mVec;
};
void testFn()
{
Test t;
t.add( 3, 4 );
t.add( 2, 5 );
TestBase* tB = &t;
Iter<Widget> iter = tB->getIter();
Widget* pW;
while ( pW = iter.next() )
{
std::cout << "px: " << pW->px << " py: " << pW->py << std::endl;
}
}
Это должно делать то, что вы хотите:
typedef typename std::vector<T>::iterator MyIterator;
От Ускоренный C++:
Всякий раз, когда у вас есть тип, например
vector<T>
, это зависит от параметра шаблона, и вы хотите использовать член этого типа, напримерsize_type
, которое само по себе является типом, перед всем именем необходимо поставитьtypename
чтобы сообщить реализации, что имя следует рассматривать как тип.
Я не уверен, что вы подразумеваете под «не раскрывать std::vector публично», но на самом деле вы можете просто определить свой typedef следующим образом:
typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator; // To work with constant references
Вы сможете изменить эти определения типов позже, и пользователь ничего не заметит...
Кстати, хорошей практикой считается также предоставлять еще несколько типов, если вы хотите, чтобы ваш класс вел себя как контейнер:
typedef typename std::vector<T>::size_type size_type;
typedef typename std::vector<T>::difference_type difference_type;
typedef typename std::vector<T>::pointer pointer;
typedef typename std::vector<T>::reference reference;
И если это необходимо вашему классу:
typedef typename std::vector<T>::const_pointer const_pointer;
typedef typename std::vector<T>::const_reference const_reference;
Вы найдете значение всех этих typedef здесь: STL-документация по векторам
Редактировать:Добавлен typename
как предложено в комментариях