Как 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, является конец файла.
- Обратите внимание, что технически это не соответствует поведению. Я упростила сравнение, чтобы все было просто. Два построенных по умолчанию итератора должны сравнивать равные, и если вы строите два итератора из одного и того же потока, они должны сравнивать равные, по крайней мере, перед тем, как вы что-нибудь прочитаете из потока. Это практически не имеет практического значения - единственное сравнение, которое вы видели в реальном использовании, это определить, достигли ли вы конца файла.
Часть ответа ниже процитирована из стандартной библиотеки C++: Учебное пособие и справочник Николая М. Йосуттиса с небольшими изменениями.
Выражение
istream_iterator<string>(cin)
создает строковый итератор, который читает из стандартного потока ввода
cin
, Аргумент шаблонаstring
указывает, что потоковый итератор читает элементы этого типа. Эти элементы читаются с помощью обычного оператора ввода >>. Таким образом, каждый раз, когда алгоритм хочет обработать следующий элемент, итератор istream преобразует это желание в вызовcin >> string
Оператор ввода для строк обычно читает одно слово, разделенное пробелами.
Выражение
istream_iterator<string>()
вызывает конструктор по умолчанию для итераторов istream, который создает так называемый итератор конца потока. Он представляет собой поток, из которого вы больше не можете читать. Итератор конца строки используется как
end of the range
Итак, алгоритмcopy
читает все строки изcin
пока он больше не может читать.
Последний:
back_inserter(col))
согласно документации back_inserter:
Std::back_insert_iterator, который можно использовать для добавления элементов в конец контейнера.
Это добавит все прочитанное в строках в col
,
Вы можете найти информацию о std::istream_iterator и std::back_inserter для деталей.