Есть некоторый генератор, который выдает значения.
Его интерфейс описывается тремя функциями:
void Next(); // Сгенерировать значение
bool Done() const; // Значения закончились
T& Value(); // Текущее значение, movable
Получение значений в цикле выглядит так:
Generator
';
gen.Next();
}
Как сделать итератор, чтобы можно было писать
for (T& x : gen) std::cout << x << '
';
итератор должен поддержисать интерфейс InputIterator
Ответ
Итератор можно добавить через пару свободных функций begin и end.
#include
template
using iterator = GeneratorIterator
// Требования Iterator:
GeneratorIterator(const iterator&) = default; // CopyConstructible
iterator& operator=(const iterator&) = default; // CopyAssignable
~GeneratorIterator() = default; // Destructible
friend void swap(iterator& a, iterator& b) { std::swap(a.g_, b.g_); } // swappable
// Требования InputIterator:
friend bool operator==(const iterator& lhs, const iterator& rhs) { return lhs.g_ == rhs.g_; } // EqualityComparable
friend bool operator!=(const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); } // a != b
reference operator*() const { // *a
return g_->Value();
}
pointer operator->() const { return std::addressof(**this); } // a->m
iterator& operator++() { // ++r
g_->Next();
if (g_->Done()) g_ = nullptr;
return *this;
}
struct proxy {
T value_;
proxy(T& value) : value_(std::move(value)) {}
reference operator*() { return value_; }
};
proxy operator++(int) { // (void)r++ , *r++
proxy old{**this};
++*this;
return old;
}
};
template
template
Поддержка std::iterator_traits обязательна для всех итераторов. Т.к. вспомогательный класс std::iterator объявлен устаревшим в С++17, то мы не будем его использовать и напишем все 5 типов сами.
Требования Iterator это CopyConstructible, CopyAssignable, Destructible, swappable, а также *r и ++r которые перекрываются InputIterator.
Требования InputIterator это Iterator, EqualityComparable с дополнительным a != b, также *a, a->m, ++r, (void)r++ и *r++
Требование *r++ самое сложное, т.к. требует вспомогательного класса куда надо переместить значение. Если значение некопируемое, то от этого надо отказаться и оставить только поддержку (void)r++
InputIterator не гарантирует что если a == b, то ++a == ++b
Поэтому в данной реализации также не гарантируется что если a == b, то ++a == b
т.е. инкремент итератора инвалидирует его копии.
Это позволяет не писать (lhs.g_ && lhs.g_->Done()) == (rhs.g_ && rhs.g_->Done()) в операторе сравнения.
Комментариев нет:
Отправить комментарий