Я пытаюсь реализовать шаблон итератора, как это делается в стандартных контейнерах библиотеки, то есть создание отдельного класса 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*
, но моя цель - реализовать итератор и реально увидеть, как эти двапонятия (контейнеры и итераторы) работают вместе в стандартной библиотеке. Таким образом, я что-то упускаю в реализации итератора и не могу выяснить, что это такое.