Реализация шаблона Iterator как стандартного соответствующего класса со ссылкой на контейнер в C ++ - PullRequest
0 голосов
/ 03 октября 2019

Я пытаюсь реализовать шаблон итератора, как это делается в стандартных контейнерах библиотеки, то есть создание отдельного класса Iterator, а не просто с помощью typedef, например using iterator = T*;. Я хочу понять связь между контейнерами и итераторами в стандартной библиотеке. Я написал следующую программу, которая напоминает структуру std::array и имеет вложенный класс Iterator внутри. Я в меру своих возможностей следую названным требованиям LegacyRandomAccessIterator из cppreference .

Я также хочу включить функцию цикла на основе диапазона для контейнера, поэтомуЯ реализую методы begin и end. Они возвращают Iterator сейчас, а не просто T*.

#include <algorithm>
#include <chrono>
#include <iostream>
#include <memory>
#include <random>

template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
class MyArray
{
    std::size_t m_size;
    std::size_t m_capacity;
    std::unique_ptr<T[]> m_data;
public:
    using value_type = T;
    using size_type = std::size_t;
    using pointer = T*;
    using const_pointer = T* const;
    using void_pointer = void*;
    using const_void_pointer = void* const;
    using difference_type = std::ptrdiff_t;
    using reference = T&;
    using const_reference = const T&;

    constexpr MyArray(std::size_t capacity)
        : m_size{0},
        m_capacity{capacity},
        m_data{std::make_unique<T[]>(capacity)}
    {
        std::size_t m_seed = std::chrono::steady_clock::now().time_since_epoch().count();
        std::default_random_engine m_reng{m_seed};
        // fill up the array with random data
        for (std::size_t i = 0; i < m_size; ++i)
            m_data[i] = m_reng();
    }

    class Iterator
    {
        MyArray* v_;
        std::size_t m_index;
    public:
        using iterator_category = typename std::random_access_iterator_tag;
        using value_type = MyArray::value_type;
        using pointer = MyArray::pointer;
        using difference_type = MyArray::difference_type;
        using reference = MyArray::reference;

        Iterator(MyArray* v_, std::size_t index)
            : v_{ v_ },
            m_index{ index }
        {}

        //operator std::size_t() noexcept {
        //  return m_index;
        //}

        T& operator*() {
            return (*v_)[m_index];
        }
        const T& operator*() const {
            return (*v_)[m_index];
        }
        T* operator->() {
            return &((*v_)[m_index]);
        }
        const T* operator->() const {
            return &((*v_)[m_index]);
        }
        T& operator[](difference_type m) {
            return (*v_)[m_index + m];
        }
        const T& operator[](difference_type m) const {
            return (*v_)[m_index + m];
        }

        /// \brief pre-inc/dec
        Iterator& operator++() noexcept {
            ++m_index;
            return *this;
        }
        Iterator& operator--() noexcept {
            --m_index;
            return *this;
        }
        /// \brief post-inc/dec
        Iterator operator++(int) {
            Iterator r{ *this };
            ++m_index;
            return r;
        }
        Iterator operator--(int) {
            Iterator r{ *this };
            --m_index;
            return r;
        }

        Iterator& operator+=(difference_type n) noexcept {
            m_index += n;
            return *this;
        }
        Iterator& operator-=(difference_type n) noexcept {
            m_index -= n;
            return *this;
        }

        Iterator operator+(difference_type n) const {
            Iterator r{ *this };
            return r += n;
        }
        Iterator operator-(difference_type n) const {
            Iterator r{ *this };
            return r -= n;
        }

        difference_type operator-(Iterator const& r) const noexcept {
            return m_index - r.m_index;
        }

        /// \brief comparisons
        inline constexpr bool operator<(const Iterator& it)  const noexcept { return m_index < it.m_index; }
        inline constexpr bool operator<=(const Iterator& it) const noexcept { return m_index <= it.m_index; }
        inline constexpr bool operator>(const Iterator& it)  const noexcept { return m_index > it.m_index; }
        inline constexpr bool operator>=(const Iterator& it) const noexcept { return m_index >= it.m_index; }
        inline constexpr bool operator!=(const Iterator& it) const noexcept { return m_index != it.m_index; }
        inline constexpr bool operator==(const Iterator& it) const noexcept { return m_index == it.m_index; }
    };

    using iterator      = Iterator;
    using citerator     = const iterator;

    iterator begin() noexcept {
        std::cout << __func__ << '\n';
        return &m_data[0];
    }
    iterator end() noexcept {
        std::cout << __func__ << '\n';
        return &m_data[m_size - 1];
    }
    const iterator cbegin() const noexcept {
        std::cout << __func__ << '\n';
        return &m_data[0];
    }
    const iterator cend() const noexcept {
        std::cout << __func__ << '\n';
        return &m_data[m_size - 1];
    }
    T operator[](const std::size_t index) const noexcept {
        std::cout << __func__ << '\n';
        return m_data[index];
    }
};

main.cpp для тестов:

int main()
{
    MyArray<int> v{20};
    for (auto it : v) {
        std::cout << it << ' ';
    }
    std::cout << '\n';
    for (const auto cit : v) {
        std::cout << cit << ' ';
    }
    std::cout << '\n';
    for (auto& itr : v) {
        std::cout << itr << ' ';
    }
    std::cout << '\n';
    for (const auto& citr : v) {
        std::cout << citr << ' ';
    }

    std::cout << "\nfor_each" << '\n';
    std::for_each(v.begin(), v.end(), [](int arg)
        {
            std::cout << arg << ' ';
        }
    );

    std::cout << "\n\n";
    std::cout << v[2] << '\n';
    std::cout << v[5] << '\n';
    std::cout << v[8] << '\n';

    return 0;
}

Я получаю ошибку:

error C2440: 'return': cannot convert from 'T' to 'T &'
with
[
    T=int
]
note: while compiling class template member function 'T &MyArray<T,void>::Iterator::operator *(void)'

, который указывает здесь в этой функции класс Iterator:

T& operator*() {
    return (*v_)[m_index];
}

Если мы будем следовать указанной странице требований на cppreference, мы окажемся в LegacyInputIterator , который реализует *i операция разыменования, которая говорит, что возвращаемое значение должно быть reference, convertible to value_type.

Программа работает, если я заменю Iterator на T*, но моя цель - реализовать итератор и реально увидеть, как эти двапонятия (контейнеры и итераторы) работают вместе в стандартной библиотеке. Таким образом, я что-то упускаю в реализации итератора и не могу выяснить, что это такое.

...