[c++] Как реализовать итератор стиля STL и избежать распространенных ошибок?


2 Answers

Документация iterator_facade от Boost.Iterator предоставляет то, что выглядит как хороший учебник по внедрению итераторов для связанного списка. Не могли бы вы использовать это как отправную точку для создания итератора с произвольным доступом по вашему контейнеру?

Если ничего другого, вы можете взглянуть на функции-члены и typedefs, предоставленные iterator_facade и использовать его в качестве отправной точки для создания собственного.

Question

Я создал коллекцию, для которой я хочу предоставить итератор с произвольным доступом в стиле STL. Я искал пример реализации итератора, но я не нашел его. Я знаю о необходимости перегрузки констант операторов [] и * . Каковы требования для того, чтобы итератор был «STL-style», и каковы некоторые другие ошибки, которых следует избегать (если они есть)?

Дополнительный контекст: это для библиотеки, и я не хочу вводить какую-либо зависимость от нее, если мне это действительно нужно. Я пишу свою собственную коллекцию, чтобы иметь возможность поддерживать двоичную совместимость между C ++ 03 и C ++ 11 с тем же компилятором (поэтому STL, который, вероятно, не сломается).




Я был на той же лодке, что и вы по разным причинам (частично образовательные, частично ограниченные). Мне пришлось переписать все контейнеры стандартной библиотеки, и контейнеры должны были соответствовать стандарту. Это означает, что если я поменяю свой контейнер на stl- версию, код будет работать одинаково. Это также означало, что мне пришлось перезаписать итераторы.

Во всяком случае, я посмотрел на EASTL . Помимо изучения тонны о контейнерах, которые я никогда не узнавал все это время, используя контейнеры stl или через мои курсы бакалавриата. Основная причина в том, что EASTL более читабельна, чем stl- аналог (я обнаружил, что это просто из-за отсутствия всех макросов и прямого стиля кодирования). Там есть какие-то нехорошие вещи (например, #ifdefs для исключений), но ничего не подавить вас.

Как упоминалось выше, посмотрите ссылку cplusplus.com на итераторы и контейнеры.




Вот пример исходного итератора указателя.

Вы не должны использовать класс iterator для работы с необработанными указателями!

#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <assert.h>

template<typename T>
class ptr_iterator
    : public std::iterator<std::forward_iterator_tag, T>
{
    typedef ptr_iterator<T>  iterator;
    pointer pos_;
public:
    ptr_iterator() : pos_(nullptr) {}
    ptr_iterator(T* v) : pos_(v) {}
    ~ptr_iterator() {}

    iterator  operator++(int) /* postfix */         { return pos_++; }
    iterator& operator++()    /* prefix */          { ++pos_; return *this; }
    reference operator* () const                    { return *pos_; }
    pointer   operator->() const                    { return pos_; }
    iterator  operator+ (difference_type v)   const { return pos_ + v; }
    bool      operator==(const iterator& rhs) const { return pos_ == rhs.pos_; }
    bool      operator!=(const iterator& rhs) const { return pos_ != rhs.pos_; }
};

template<typename T>
ptr_iterator<T> begin(T *val) { return ptr_iterator<T>(val); }


template<typename T, typename Tsize>
ptr_iterator<T> end(T *val, Tsize size) { return ptr_iterator<T>(val) + size; }

Обходное окружение на основе диапазона Raw pointer. Пожалуйста, исправьте меня, если есть лучший способ сделать цикл, основанный на диапазоне, от необработанного указателя.

template<typename T>
class ptr_range
{
    T* begin_;
    T* end_;
public:
    ptr_range(T* ptr, size_t length) : begin_(ptr), end_(ptr + length) { assert(begin_ <= end_); }
    T* begin() const { return begin_; }
    T* end() const { return end_; }
};

template<typename T>
ptr_range<T> range(T* ptr, size_t length) { return ptr_range<T>(ptr, length); }

И простой тест

void DoIteratorTest()
{
    const static size_t size = 10;
    uint8_t *data = new uint8_t[size];
    {
        // Only for iterator test
        uint8_t n = '0';
        auto first = begin(data);
        auto last = end(data, size);
        for (auto it = first; it != last; ++it)
        {
            *it = n++;
        }

        // It's prefer to use the following way:
        for (const auto& n : range(data, size))
        {
            std::cout << " char: " << static_cast<char>(n) << std::endl;
        }
    }
    {
        // Only for iterator test
        ptr_iterator<uint8_t> first(data);
        ptr_iterator<uint8_t> last(first + size);
        std::vector<uint8_t> v1(first, last);

        // It's prefer to use the following way:
        std::vector<uint8_t> v2(data, data + size);
    }
    {
        std::list<std::vector<uint8_t>> queue_;
        queue_.emplace_back(begin(data), end(data, size));
        queue_.emplace_back(data, data + size);
    }
}



Related