Как правильно реализовать пользовательские итераторы и const_iterator?

У меня есть собственный класс контейнера, для которого я хотел бы написать классы итератора итератора и const_iterator .

Я никогда не делал этого раньше, и мне не удалось найти подходящие инструкции. Каковы рекомендации по созданию итератора и что мне следует знать?

Я также хотел бы избежать дублирования кода (я чувствую, что const_iterator и итератор имеют много общего ; следует ли одно подклассифицировать другое?).

Примечание: я почти уверен, что в Boost есть кое-что, чтобы облегчить это, но я не могу использовать его здесь по многим глупым причинам.

217
задан svick 7 July 2013 в 04:39
поделиться

4 ответа

  • Выберите тип итератора, который подходит для вашего контейнера: ввод, вывод, пересылка и т. д.
  • Используйте базовые классы итераторов из стандартной библиотеки. Например, 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. См. соответствующее обсуждение здесь .

148
ответ дан 23 November 2019 в 04:16
поделиться

Я не знаю, есть ли в 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;
16
ответ дан 23 November 2019 в 04:16
поделиться

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 его.

23
ответ дан 23 November 2019 в 04:16
поделиться

Часто забывают, что 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, это преобразование никогда не используется.

22
ответ дан 23 November 2019 в 04:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: