Используйте класс итератора, который абстрагирует эту функциональность.
Итератор имеет 3 основных функции:
- Increment
- разыменования
- Проверка на равенство
Мы можем использовать их в качестве руководства при создании интерфейса, который абстрагирует это поведение. Этот интерфейс немного громоздок в использовании сам по себе, но мы можем использовать его для создания класса-оболочки GenericIterator
, который может автоматически назначаться любому другому типу итератора.
Можно написать класс GenericIterator
, которому можно назначить итераторы практически из любой коллекции, включая обратные итераторы.
int main() {
bool iterate_forward;
std::cin >> iterate_forward;
std::vector<int> values { 1, 2, 3 };
GenericIterator<int&> begin, end;
if(iterate_forward) {
begin = values.begin();
end = values.end();
} else {
begin = values.rbegin();
end = values.rend();
}
// Print out the values
for(; begin != end; ++begin) {
std::cout << *begin << " ";
}
}
Вы можете загрузить весь код из этого репозитория github , который я буду обновлять и улучшать по мере необходимости.
Замечания по производительности GenericIterator
С точки зрения функциональности, GenericIterator
дает вам все, что вы могли бы попросить. Это легкий вес; и это удобно; и это легко изменить, если ваш код должен быть прочитан из std::list
или чего-то другого, кроме вектора.
Однако из-за фундаментальных ограничений полиморфизма во время выполнения компилятору значительно сложнее встроить вызовы виртуальных методов. Это означает, что GenericIterator
несет больше накладных расходов во время выполнения, чем другие решения.
Хорошая идея отдавать предпочтение статическому полиморфизму и шаблонам по сравнению с полиморфизмом времени исполнения, когда это возможно. Если вы можете сделать это, используйте что-то вроде решения Mark B , которое в конечном итоге будет более производительным.
Приложение
Определение интерфейса итератора. Этот класс используется для реализации GenericIterator
. GenericIterator
содержит указатель на IteratorBase
, который используется для достижения полиморфизма во время выполнения. Благодаря методу clone()
, GenericIterator
остается копируемым и подвижным, как и ожидалось.
template <class Value>
class IteratorBase
{
public:
virtual Value operator*() const = 0;
virtual IteratorBase& operator++() = 0;
virtual bool operator!=(IteratorBase const&) const = 0;
virtual bool operator==(IteratorBase const&) const = 0;
// We need this function for making copies of the iterator
virtual IteratorBase* clone() const = 0;
virtual ~IteratorBase() = default;
};
Конкретный класс, реализующий IteratorBase
. Этот класс реализует поведение, определенное в IteratorBase
. Содержащийся в нем итератор является фактическим итератором, возвращаемым коллекцией, для которой вы выполняете итерацию. В вашем случае это будет std::vector::iterator
или std::vector::reverse_iterator
.
template <class Iter, class Value>
class IteratorDerived : public IteratorBase<Value>
{
Iter it;
public:
IteratorDerived() = default;
IteratorDerived(Iter it) : it(it) {}
IteratorDerived(IteratorDerived const&) = default;
IteratorDerived(IteratorDerived&&) = default;
Value operator*() const override { return *it; }
IteratorBase<Value>& operator++() override
{
++it;
return *this;
}
bool operator!=(IteratorBase<Value> const& other) const override
{
auto* derived = dynamic_cast<IteratorDerived const*>(&other);
return derived == nullptr || it != derived->it;
}
bool operator==(IteratorBase<Value> const& other) const override
{
auto* derived = dynamic_cast<IteratorDerived const*>(&other);
return derived != nullptr && it == derived->it;
}
IteratorBase<Value>* clone() const override
{
return new IteratorDerived(*this);
}
};
GenericIterator
реализация. Это фактическая реализация GenericIterator
, основанная на IteratorBase
и IteratorDerived
. Любой итератор, заданный для GenericIterator
, помещается в соответствующий IteratorDerived
, который затем назначается указателю IteratorBase
.
template <class Value>
class GenericIterator
{
std::unique_ptr<IteratorBase<Value>> iterator;
public:
using value_type = typename std::remove_reference<Value>::type;
using reference = Value;
GenericIterator() = default;
GenericIterator(GenericIterator const& it) : iterator(it.iterator->clone())
{
}
GenericIterator(GenericIterator&&) = default;
// Creates a GenericIterator from an IteratorBase
explicit GenericIterator(IteratorBase<Value> const& it)
: iterator(it.clone())
{
}
// Creates a GenericIterator from an IteratorDerived
template <class Iter>
explicit GenericIterator(IteratorDerived<Iter, Value> const& it)
: iterator(it.clone())
{
}
// Creates a GenericIterator by wrapping another Iter
template <class Iter>
GenericIterator(Iter it) : iterator(new IteratorDerived<Iter, Value>(it))
{
}
GenericIterator& operator=(GenericIterator const& it)
{
iterator = std::unique_ptr<IteratorBase<Value>>(it.iterator->clone());
return *this;
}
GenericIterator& operator=(GenericIterator&&) = default;
Value operator*() const { return *(*iterator); }
GenericIterator& operator++()
{
++(*iterator);
return *this;
}
void operator++(int) {
++(*iterator);
}
bool operator==(GenericIterator const& other) const
{
return *iterator == *other.iterator;
}
bool operator!=(GenericIterator const& other) const
{
return *iterator != *other.iterator;
}
};