Страницы

Поиск по вопросам

среда, 12 июня 2019 г.

Как написать InputIterator?

Есть некоторый генератор, который выдает значения. Его интерфейс описывается тремя функциями:
void Next(); // Сгенерировать значение bool Done() const; // Значения закончились T& Value(); // Текущее значение, movable
Получение значений в цикле выглядит так:
Generator gen = ...; gen.Next(); while (!gen.Done()) { std::cout << gen.Value() << '
'; gen.Next(); }
Как сделать итератор, чтобы можно было писать
for (T& x : gen) std::cout << x << '
';
итератор должен поддержисать интерфейс InputIterator


Ответ

Итератор можно добавить через пару свободных функций begin и end.
#include #include
template struct GeneratorIterator { // Поддержка std::iterator_­traits: using iterator_category = std::input_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&;
using iterator = GeneratorIterator; // Более короткое имя, для удобства Generator* g_; // Ссылка на генератор, он должен жить дольше итератора GeneratorIterator(Generator* g) : g_(g) {} // Конструктор
// Требования 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 GeneratorIterator begin(Generator& gen) { GeneratorIterator it{&gen}; return ++it; }
template GeneratorIterator end(Generator&) { return {nullptr}; }
Поддержка 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()) в операторе сравнения.

Комментариев нет:

Отправить комментарий