Требования для InputIterator включают *i++
с эквивалентным выражением, являющимся
value_type x = *i;
++i;
return x;
Как можно объявить такого оператора без применения стандартного постинкрементного i++
возвращая не пустое значение (какие InputIterators не обязаны делать)?
Вы можете использовать прокси для увеличения поста:
#include <iostream>
class input_iterator
{
private:
class post_increment_proxy
{
public:
post_increment_proxy(int value) : value(value) {}
int operator * () const { return value; }
private:
int value;
};
public:
post_increment_proxy operator ++ (int) {
post_increment_proxy result{value};
++value;
return result;
}
private:
int value = 0;
};int main() {
input_iterator i;
std::cout << *i++ << '\n';
std::cout << *i++ << '\n';
std::cout << *i++ << '\n';
}
Прежде всего, итератор является копируемым, хотя в случае InputIterator копия действует скорее как перемещение (в частности, после того, как вы увеличиваете любую одну копию итератора, вы не должны разыменовывать любую другую ее копию).
Тем не менее, не должно быть никаких проблем с копированием итератора — фактически, большая часть библиотеки (и много другого кода) предполагает, что итераторы являются «легковесными» объектами; их копирование обходится дешево, поэтому (для одного очевидного примера) они обычно передаются по значению, а не по ссылке.
Итак, несколько упрощенный потоковый итератор может выглядеть примерно так:
template <class T>
class istream_iterator {
std::istream *is;
T data;
public:
istream_iterator(std::istream &is) : is(&is) { ++(*this); }
istream_iterator() : is(nullptr) { }
istream_iterator &operator++() { (*is) >> data; return *this; }
// So here's the post-increment: it just saves a copy of itself, then
// reads the next item (which increments the iterator), and finally
// returns the copied object, which will return the previously-read item
// from the stream when/if dereferenced.
istream_iterator operator++(int) {
// Note: this uses the compiler-generated copy constructor. Assuming
// a `T` is copy-constructible, this works fine--other than a T,
// we're only copying a pointer.
istream_iterator temp = *this;
(*is) >> data;
return temp;
}
T const &operator*() const { return data; }
bool operator !=(istream_iterator &end) { return (*is).good(); }
bool operator ==(istream_iterator &end) { return !(*is).good(); }
};
Это «обманывает» пару довольно незначительных моментов — например, два построенных по умолчанию итератора должны сравниваться друг с другом, что не мешает их реализовать (и которые практически никто никогда не использует и не заботится). При обычном использовании вы создаете один итератор из потока, а по умолчанию создаете другой. Сравнение между ними должно возвращать true, если и только если первый достиг конца входного потока (или чтение по какой-то причине не удалось). Аналогично, это исключает реализацию operator->
и некоторые из typedef
Требуется стандартного типа итератора (value_type, traits_type, istream_type и т. д.). Однако ни один из них не имеет отношения к рассматриваемому вопросу (и все это вопрос добавления необходимого кода, а не внесения каких-либо существенных изменений в уже существующий код Вот).
Небольшая демонстрация кода может выглядеть примерно так:
int main() {
istream_iterator<char> i(std::cin), end;
while (i != end)
std::cout << *i++;
}
Это скопирует символы из стандартного ввода в стандартный вывод, пропуская пробел, потому что operator>>
по умолчанию пропускаются пробелы (но вы можете устранить это с помощью noskipws
если ты хочешь).