Как работает std:: copy с итераторами потока

обычная конструкция STL:

vector<string> col;
copy(istream_iterator<string>(cin), istream_iterator<string>(),
    back_inserter(col));

где мы используем istream_iterator копировать из std ввода (cin) в вектор.

может ли кто-нибудь объяснить, как работает этот код?

моя проблема в том, что я действительно не понимаю эту часть:

istream_iterator<string>(cin), istream_iterator<string>()

2 ответов


во-первых, обратите внимание, что в этом случае, нет никакой реальной необходимости использовать std::copy на всех. Вы можете просто инициализировать вектор непосредственно из итераторов:

vector<string> col((istream_iterator<string>(cin)),
                    istream_iterator<string>());

это, вероятно, не делает код намного легче понять.

что касается того, как работает код, это, вероятно, немного более прямолинейно, чем вы думаете. Istream_iterator выглядит примерно так:

template <class T>
class istream_iterator { 
    std::istream *is;
    T data;
public:
    istream_iterator(std::istream &is) : is(&is) { ++(*this); }
    istream_iterator() : is(nullptr) {}

    T operator++() { (*is) >> data; return *this; }
    T operator++(int) { (*is) >> data; return *this; }

    T const &operator*() { return data; }   

    bool operator !=(istream_iterator &end) { return (*is).good(); }
    bool operator ==(istream_iterator &end) { return !(*is).good(); }
};

очевидно, что я пропускаю больше, но это больше всего о том, что нас здесь волнует. Итак, что происходит, когда вы создаете итератор, он читает (или пытается) элемент из потока в переменную, которую я назвал data. Когда вы разыменовываете итератор, он возвращает data. При увеличении итератора он считывает (или пытается) следующий элемент из файла. Несмотря на то, что они написаны так, как будто сравнивают один итератор с другим,operator== и operator!= действительно просто проверьте конец файла1.

вот тут используется std::copy, который (снова упрощенный) выглядит смутно следующим образом:

template <class InIt, class OutIt>
void std::copy(InIt b, InIt e, OutIt d) { 
    while (b != e) {
        *d = *b;
        ++b;
        ++d;
    }
}

Итак, это считывает и элемент из входного итератора, записывает этот элемент в выходной итератор и повторяется до тех пор, пока итератор для текущей позиции не сравняется с итератором для конца ввода (что произойдет, когда вы достигнете конца файла). Обратите внимание, что в отличие от других итераторов, единственная "конечная" позиция, которую вы можете использовать с итератором istream, - это конец файл.


  1. обратите внимание, что технически, это не соответствующее поведение. Я упростил сравнение, чтобы все было просто. Два итератора, построенные по умолчанию, должны сравниваться равными, и если вы создаете два итератора из одного потока, они должны сравнивать равными, по крайней мере, до того, как вы прочитали что-либо из потока. Это не имеет большого практического значения - единственное сравнение, которое вы видели в реальном использовании, - это определить, достигли ли вы конца файла еще.

часть ответа ниже цитируется из стандартной библиотеки C++: учебник и ссылка Николая М. Josuttis с небольшими изменениями.

выражение

  istream_iterator<string>(cin)  

создает строковый итератор, который считывает данные из стандартного входного потока cin. Аргумент шаблона string указывает, что итератор потока считывает элементы этого типа. Эти элементы считываются с помощью обычного оператора ввода >>. Таким образом, каждый раз, когда алгоритм хочет обработайте следующий элемент, итератор istream преобразует это желание в вызов

cin >> string  

оператор ввода для строк обычно считывает одно слово, разделенное пробелами.

выражение

istream_iterator<string>()  

вызывает конструктор по умолчанию итераторов istream, который создает так называемый итератор конца потока. Он представляет собой поток, из которого вы больше не можете читать. Итератор конца строки используется как end of the range, так что алгоритм copy читает все строки из cin пока он больше не может читать.

последний вопрос:

back_inserter(col))

согласно документации back_inserter:

a std:: back_insert_iterator, который можно использовать для добавления элементов в конец контейнера c

он добавит все прочитанные строки в col.

вы можете найти информацию о std:: istream_iterator и std:: back_inserter для сведения.