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

277
25 июля 2017, 19:05

Есть некоторый генератор, который выдает значения.
Его интерфейс описывается тремя функциями:

void Next();        // Сгенерировать значение
bool Done() const;  // Значения закончились
T& Value();         // Текущее значение, movable

Получение значений в цикле выглядит так:

Generator<T> gen = ...;
gen.Next();
while (!gen.Done()) {
  std::cout << gen.Value() << '\n';
  gen.Next();
}

Как сделать итератор, чтобы можно было писать

for (T& x : gen) std::cout << x << '\n';

итератор должен поддержисать интерфейс InputIterator.

Answer 1

Итератор можно добавить через пару свободных функций begin и end.

#include <iterator>
#include <utility>
template<typename T>
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<T>;         // Более короткое имя, для удобства
  Generator<T>* g_;                              // Ссылка на генератор, он должен жить дольше итератора
  GeneratorIterator(Generator<T>* 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<typename T>
GeneratorIterator<T> begin(Generator<T>& gen) {
  GeneratorIterator<T> it{&gen};
  return ++it;
}
template<typename T>
GeneratorIterator<T> end(Generator<T>&) {
  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++.

READ ALSO
Как получит HWND окна при наведении на него курсора мыши?

Как получит HWND окна при наведении на него курсора мыши?

Как получить HWND окна при наведении на него курсора мыши С++ WinApi32 или Qt?

233
Почему строковый литерал в С++ это l-value?

Почему строковый литерал в С++ это l-value?

Например строковый литерал: "Hello, world!" не может стоять слева от оператора присваиванияТогда, как он может являться леводопустимым выражением?

297
Добавить класс к ссылке

Добавить класс к ссылке

Есть менюНужно, чтобы при нажатии на любой из пунктов меню - он подчеркивался черной линией

330
Добавление jquery в yii2

Добавление jquery в yii2

ЗдравствуйтеЯ чет торможу, не могу найти в документации как подключить jquery в Yii2

374