2016-02-02 2 views
-1

Requirements для InputIterator включают *i++ с эквивалентным выражением будучиКак реализовать «разыменование и пост-инкремент» для ввода итератора в C++?

value_type x = *i; 
++i; 
return x; 

Как можно объявить такой оператор без реализации стандартного пост-приращение i++ возвращает непустое значение (которое InputIterators не обязаны делать)?

+1

Почему '' я ++ требует 'i' быть копируемыми? Во всяком случае, итератор, являющийся копируемым, не будет проблемой. – juanchopanza

+1

Если это какой-то итератор потока, который генерирует только одно значение за раз и отбрасывает его, вы можете сделать его скопируемым, сохранив значение в итераторе. Затем копия итератора может вернуть значение, которое входной поток имел во время копирования итератора. –

+0

@ZanLynx Но это означает, что 'i ++' должен возвращать копию итератора, а не void? Как я понимаю, InputIterators нужно только предоставить '(void) i ++', а не полный 'i ++'. Отсюда мой вопрос. –

ответ

3

Вы можете использовать прокси-сервер для почтового приращения:

#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'; 
} 
+0

Вы, конечно, не можете использовать это для реализации 'std :: istream_iterator' (поскольку он указывает, что тип возврата из' operator ++ (int) 'является' istream_iterator', а не прокси-тип. –

+0

@JerryCoffin Требование: ' void) i ++ ' –

+0

Ummm ... что? Требование, в n4296, [istrream.iterator]:' istream_iterator operator ++ (int); ' –

1

Прежде всего, итератор является скопируемым, хотя в случае 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 s требуются от стандартного типа итератора (value_type, traits_type, istream_type и т. Д.). Ни одно из них не имеет отношения к рассматриваемому вопросу (и все это вопрос добавив требуемый код, не внося существенных изменений в код, который уже есть).

Быстрый демо-кода может выглядеть следующим образом:

int main() { 
    istream_iterator<char> i(std::cin), end; 

    while (i != end) 
     std::cout << *i++; 
} 

Это будет копировать символы из стандартного ввода на стандартный вывод, пропуская пробельные, потому что operator>> пропускает пробелы по умолчанию (но вы можете исключить, что с noskipws, если хотите).

+0

Что такое подпись для этого кода? –