У меня есть собственный класс контейнера, для которого я хотел бы написать классы итератора итератора
и const_iterator
.
Я никогда не делал этого раньше, и мне не удалось найти подходящие инструкции. Каковы рекомендации по созданию итератора и что мне следует знать?
Я также хотел бы избежать дублирования кода (я чувствую, что const_iterator
и итератор
имеют много общего ; следует ли одно подклассифицировать другое?).
Примечание: я почти уверен, что в Boost есть кое-что, чтобы облегчить это, но я не могу использовать его здесь по многим глупым причинам.
std::iterator
с random_access_iterator_tag
. Эти базовые классы определяют все определения типов, требуемые STL, и выполняют другую работу. Во избежание дублирования кода класс итератора должен быть классом-шаблоном и параметризоваться по «типу значения», «типу указателя», «типу ссылки» или всем сразу (зависит от реализации). Например:
// класс итератора параметризуется по типу указателя
шаблон class MyIterator {
// здесь находится определение класса итератора
};
typedef MyIterator iterator_type;
typedef MyIterator const_iterator_type;
Обратите внимание на определения типов iterator_type
и const_iterator_type
: это типы для ваших неконстантных и константных итераторов.
См. также: ссылка на стандартную библиотеку
EDIT: std::iterator
устарел, начиная с C++17. См. соответствующее обсуждение здесь .
Я не знаю, есть ли в Boost что-нибудь, что могло бы помочь.
Мой предпочтительный шаблон прост: взять аргумент шаблона, который равен value_type
, либо с уточнением const, либо без него. При необходимости также тип узла. Тогда, ну, все как бы становится на свои места.
Только не забудьте параметризовать (шаблонизировать) все, что должно быть, включая конструктор копирования и оператор==
. По большей части семантика const
создаст правильное поведение.
template< class ValueType, class NodeType >
struct my_iterator
: std::iterator< std::bidirectional_iterator_tag, T > {
ValueType &operator*() { return cur->payload; }
template< class VT2, class NT2 >
friend bool operator==
( my_iterator const &lhs, my_iterator< VT2, NT2 > const &rhs );
// etc.
private:
NodeType *cur;
friend class my_container;
my_iterator( NodeType * ); // private constructor for begin, end
};
typedef my_iterator< T, my_node< T > > iterator;
typedef my_iterator< T const, my_node< T > const > const_iterator;
Boost может помочь: библиотека Boost.Iterator.
Точнее эта страница: boost::iterator_adaptor.
Что очень интересно, так это Обучающий пример, в котором показана полная реализация пользовательского типа с нуля.
template <значение класса> класс node_iter : публичный буст::iterator_adaptor< node_iter
// Получено , Значение* // База , boost::use_default // Значение , boost::forward_traversal_tag // CategoryOrTraversal > { частный: активатор структуры {}; // закрытый тип позволяет избежать неправильного использования публичный: node_iter() : node_iter::iterator_adaptor_(0) {} явный node_iter (значение * p) : node_iter::iterator_adaptor_(p) {} // итератор, конвертируемый в const_iterator, а не наоборот шаблон <класс OtherValue> node_iter( node_iter const& прочее , имя типа boost::enable_if< boost::is_convertible , активатор >::тип = активатор() ) : node_iter::iterator_adaptor_(other.base()) {} частный: класс друзей boost::iterator_core_access; void increment() { this->base_reference() = this->base()->next(); } };
Основной момент, как уже упоминалось, заключается в использовании единой реализации шаблона и typedef
его.
Часто забывают, что iterator
должен преобразовываться в const_iterator
, а не наоборот. Вот как это сделать:
template<class T, class Tag = void>
class IntrusiveSlistIterator
: public std::iterator<std::forward_iterator_tag, T>
{
typedef SlistNode<Tag> Node;
Node* node_;
public:
IntrusiveSlistIterator(Node* node);
T& operator*() const;
T* operator->() const;
IntrusiveSlistIterator& operator++();
IntrusiveSlistIterator operator++(int);
friend bool operator==(IntrusiveSlistIterator a, IntrusiveSlistIterator b);
friend bool operator!=(IntrusiveSlistIterator a, IntrusiveSlistIterator b);
// one way conversion: iterator -> const_iterator
operator IntrusiveSlistIterator<T const, Tag>() const;
};
В примечании выше показано, как IntrusiveSlistIterator
преобразуется в IntrusiveSlistIterator
. Если T
уже равно const
, это преобразование никогда не используется.