С++ подсчитывает количество слов из стандартного ввода

Я видел фрагмент кода C++ для подсчета количества слов, введенных из стандартного ввода:

#include <iostream>
#include <iterator>
#include <string>
using namespace std;

int main() {
    auto ss {
        istream_iterator<string>{cin}
    };
    cout << distance(ss, {}) << endl;

    return 0;
}

У меня есть несколько вопросов:

  1. Какой тип auto ss?
  2. Что делает distance(ss, {})? Почему он подсчитывает количество слов?

Я предполагаю:

  1. istream_iterator<string>{cin} преобразует стандартный ввод в тип istream_iterator, автоматически разделяя его пробелом (почему?). Таким образом, ss выглядит как container со всеми словами в качестве его элементов;
  2. distance(ss, {}) вычисляет расстояние между 1-м элементом и пустым (таким образом, за пределами последнего, но почему?) элементом.

Может ли кто-нибудь помочь мне разобраться с моим предположением об этом фантастическом коротком фрагменте кода?


person Heifetz_Fan    schedule 11.05.2020    source источник
comment
См. en.cppreference.com/w/cpp/iterator/istream_iterator.   -  person BessieTheCookie    schedule 11.05.2020
comment
auto ss {istream_iterator<string>{cin}}; — это просто причудливый способ сказать istream_iterator<string> ss(cin);.   -  person HolyBlackCat    schedule 11.05.2020


Ответы (2)


auto ss выводит ss как std::istream_iterator<std::string>, потому что это то, что строит полный оператор и присвоение ss.

istream_iterator использует указанный istream operator>> для чтения форматированного ввода указанного типа. , где operator>> читает ввод, разделенный пробелами, включая символы пробела, разрывы строк и т. д. Таким образом, в данном случае istream_iterator<string> читает std::string значения из std::cin один слова, разделенные пробелами, за раз.

istream_iterator – это входной итератор, а istream_iterator, построенный по умолчанию, обозначает итератор конца потока. Когда istream_iterator прекращает чтение (достигнут EOF, введен неверный ввод и т. д.), его значение становится равным итератору конца потока.

std::distance() возвращает количество элементов в диапазоне, обозначенном парой итераторов. Для неслучайного входного итератора, такого как istream_iterator, std::distance() перебирает диапазон по одному элементу за раз через operator++ итератора, подсчитывая, сколько раз он увеличивал итератор, пока не будет достигнут целевой итератор. Итак, в этом случае istream_iterator::operator++ внутренне считывает значение из своего istream , таким образом, std::distance() эффективно возвращает количество слов, успешно прочитанных из std::cin до тех пор, пока не будет достигнут конец потока.

Итак, код, который вы показали, представляет собой просто алгоритмический способ написания следующего эквивалентного цикла:

int main() {
    string s;
    size_t count = 0;
    while (cin >> s) {
        ++count;
    }
    cout << count << endl;

    return 0;
}
person Remy Lebeau    schedule 11.05.2020
comment
Спасибо @Remy за подробное объяснение! Еще один вопрос по std::distance(): зачем нам указывать пустое {} в distance(ss, {})? По en.cppreference.com/w/cpp/iterator/distance, 2-й вход должен быть InputIt last, который имеет тот же тип, что и 1-й вход InputIt first. Как тогда убедиться, что {} имеет тот же тип, что и ss? - person Heifetz_Fan; 12.05.2020

  1. ss имеет тип std::istream_iterator<std::string>.
  2. std::distance(ss, {}) вычисляет количество элементов между первым токеном с разделителями-пробелами в std::cin до конца cin, эффективно возвращая количество токенов с разделителями-пробелами в std::cin. Это связано с тем, как работает std::istream::operator>>(std::istream&, std::string&) (второй параметр на самом деле не является std::string, но я стараюсь быть кратким). Конструктор по умолчанию для std::istream_iterator<std::string> возвращает конец любого std::istream_iterator<std::string>.

Вырезание содержимого std::cin на самом деле выполняется лениво при вычислении расстояния.

Это действительно интересный фрагмент кода.

person Michaël Roy    schedule 11.05.2020