Reputation: 465
I saw a piece of C++ code to count the number of words inputted from standard input:
#include <iostream>
#include <iterator>
#include <string>
using namespace std;
int main() {
auto ss {
istream_iterator<string>{cin}
};
cout << distance(ss, {}) << endl;
return 0;
}
I have several questions:
auto ss
?distance(ss, {})
do? Why does it calculate the number of words?My guess is:
istream_iterator<string>{cin}
converts the standard input into the istream_iterator
type, automatically separated by space (why?). Thus ss
looks like a container
with all words as its elements;distance(ss, {})
calculates the distance between the 1st element and the empty (thus outside of the last, but why?) element.Can someone help me to go through my guess on this fantastic short piece of code?
Upvotes: 1
Views: 1063
Reputation: 6496
ss
has type std::istream_iterator<std::string>
.std::distance(ss, {})
computes the number of items between the first whitespace-delimited token in std::cin to the end of cin, effectively returning the number of whitespace-delimited tokens in std::cin. This is due to the way std::istream::operator>>(std::istream&, std::string&)
functions (the second parameter is not actually an std::string
, but I'm trying to keep this short). The default constructor for a std::istream_iterator<std::string>
returns the end of any std::istream_iterator<std::string>
. The cutting of the contents of std::cin is actually done lazily when computing the distance.
That is indeed an interesting piece of code.
Upvotes: 1
Reputation: 598001
auto ss
deduces ss
to be std::istream_iterator<std::string>
, because that is what the full statement is constructing and assigning to ss
.
istream_iterator
uses the specified istream
's operator>>
to read formatted input of the specified type, where operator>>
reads input delimited by whitespace, including space characters, line breaks, etc. So, in this case, istream_iterator<string>
is reading std::string
values from std::cin
one whitespace-delimited word at a time.
istream_iterator
is an input iterator, and a default-constructed istream_iterator
denotes an end-of-stream iterator. When istream_iterator
stops reading (EOF is reached, bad input is entered, etc), its value becomes equal to the end-of-stream iterator.
std::distance()
returns the number of elements in a range denoted by a pair of iterators. For a non-random input iterator, like istream_iterator
, std::distance()
iterates the range one element at a time via the iterator's operator++
, counting the number of times it incremented the iterator, until the target iterator is reached. So, in this case, istream_iterator::operator++
internally reads a value from its istream
, thus std::distance()
effectively returns how many words are successfully read from std::cin
until end-of-stream is reached.
So, the code you have shown is roughly just an algorithmic way of writing the following equivalent loop:
int main() {
string s;
size_t count = 0;
while (cin >> s) {
++count;
}
cout << count << endl;
return 0;
}
Upvotes: 4