Я сейчас разрабатываю интерфейс (Base
в следующем примере), который предлагает несколько виртуальных методов, включая begin()
а также end()
, Эти два метода просто возвращают соответствующий итератор, как и в любой другой коллекции, такой как класс. Производные классы должны реализовывать эти методы и возвращать их конкретную реализацию итератора.
В следующем (упрощенном) примере показан производный класс, который использует boost::transform_iterator
конвертировать частный внутренний список целых чисел. Эта реализация является лишь примером в реальности, итеративная «вещь» может быть чем-то другим, как и итератор.
Пример работает, но у меня есть одна проблема. Тип объекта в main()
не скрывает тот факт, что используемый итератор имеет тип TransformIterator
, Базовый класс будет использоваться в некоторой архитектуре плагинов, где каждый плагин является общей библиотекой. Плагины не должны знать, какой тип итератора используется, и должны зависеть исключительно от абстрактного интерфейса. Есть ли способ сделать это?
#include <boost/iterator/transform_iterator.hpp>
#include <iostream>
#include <string>
#include <vector>
template<class Iterator>
class Base
{
public:
virtual Iterator begin() = 0;
virtual Iterator end() = 0;
};
struct toString
{
std::string operator()(int number) const
{
return std::to_string(number);
}
};
typedef boost::transform_iterator<toString, std::vector<int>::iterator> TransformIterator;
class Derived : public Base<TransformIterator>
{
public:
Derived() : ints{132, 3, 6451, 12, 5} {}
TransformIterator begin()
{
return boost::make_transform_iterator(ints.begin(), toString());
}
TransformIterator end()
{
return boost::make_transform_iterator(ints.end(), toString());
}
private:
std::vector<int> ints;
};
int main()
{
Base<TransformIterator>* obj = new Derived();
for(const auto& value : *obj)
{
std::cout << value.size() << std::endl;
}
return 0;
}
Немного больше предыстории: этот конкретный пример основан на интерфейсе, который читает файлы конфигурации. В настоящее время я планирую дать реализацию только для файлов YAML, но возможны и другие форматы, такие как XML или INI старой школы. Таким образом, общий интерфейс.
Не с классическими итераторами C ++, нет. Они не предназначены для полиморфного использования.
Что вы можете сделать, это определить абстрактный базовый класс для ваших итераторов, который затем реализуется (шаблонной) оберткой вокруг каждого конкретного типа итератора. Абстрактный базовый класс просто определяет все необходимые операторы как чисто виртуальные. Недостатком является то, что каждая операция на итераторе будет нуждаться в вызове виртуальной функции … В зависимости от вашего использования это может или не может стать проблемой (обычно, если вы не используете их для частой итерации по очень большим коллекциям).
Пример:
template <typename T>
class InputIterator<T>
{
public:
virtual T operator*() const = 0;
// Other operators go here
};
template <typename TIterator>
class ConcreteInputIterator final
: public InputIterator<typename std::iterator_traits<TIterator>::value_type>
{
public:
ConcreteInputIterator(TIterator iterator) : m_iterator(iterator) {}
public:
virtual T operator*() const override
{
return *m_iterator;
};
private:
TIterator m_iterator;
};
Недавно я сделал нечто очень похожее в одном из моих проектов на работе. Я сделал это, представив абстрактный интерфейс итератора и класс для итератора, обращающегося к этому интерфейсу. Вот упрощенная версия:
template <class ValueType>
struct AbstractIterator
{
virtual ValueType& dereference() const = 0;
virtual void increment() = 0;
// and so on...
};template <class ValueType>
struct ClientIterator : public std::iterator<whatever>
{
std::unique_ptr<AbstractIterator<ValueType>> it;
ClientIterator(std::unique_ptr<AbstractIterator<ValueType>> it) : it(std::move(it)) {}
ValueType& operator* const () { return it->dereference(); }
ClientIterator& operator++ () { it>increment(); return *this; }
// and so on...
};
struct Base
{
typedef ClientIterator<std::string> Iterator;
virtual Iterator begin() = 0;
virtual Iterator end() = 0;
}struct Derived : Base
{
struct ServerIterator : AbstractIterator<std::string>
{
boost::transform_iterator<toString, std::vector<int>::iterator> it;
virtual std::string& dereference() const override { return *it; }
virtual void increment() override { ++it; }
}
virtual Iterator begin() override { return Iterator({new ServerIterator(something)}); }
virtual Iterator end() override { return Iterator({new ServerIterator(anotherThing)}); }
};