3

I am learning C++ through Accelerated C++ by Andrew Koenig and Barbara E. Moo. I am trying to understand how input operators work and their relation to STL algorithms.

Here is the piece of code that confuses me:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <cctype>

bool space(char c)
{
    return std::isspace(c);
}

int main()
{
    std::istream_iterator<char> i = std::find_if(
        std::istream_iterator<char>(std::cin),
        std::istream_iterator<char>(),
        space);
    std::cout << *i << std::endl;
    return 0;
}

The code compiles fine but when I run it on any input what it outputs is the last character entered. For example, I expected the output of 123 456 to be but it actually is 6. Similarly, I expected the output of 123456 to be some error because we try to access an element that doesn't exist, yet the output is again . What am I missing?

moooeeeep
  • 31,622
  • 22
  • 98
  • 187
John
  • 71
  • 6
  • 1
    `std::istream_iterator` is an input iterator. For some unclear reason, only the overloads that take an execution policy parameter are specified, according to cppreference.com, as requiring forward iterators. Without an execution policy parameter, `find_if` requires only input iterators. The example in this question proves this to be an obvious defect, in my eyes. `std::find_if` should require forward iterators, not input iterators. – Sam Varshavchik Aug 23 '19 at 12:18
  • [It seems you can use `istream_iterator()` for similar tasks.](https://en.cppreference.com/w/cpp/iterator/istream_iterator#Example) – moooeeeep Aug 23 '19 at 12:38
  • 1
    @SamVarshavchik -- I don't know if you're right that `std::find_if` should always require at least a forward iterator, but the reason that the execution policy version requires a forward iterator is that **all** algorithms that take an execution policy require forward iterators or better. Applying a parallel algorithm to a sequence designated by an input iterator just won't work; the algorithm would have to fall back on the serial version. – Pete Becker Aug 23 '19 at 12:42

1 Answers1

4

Why doesn't 123 456 as input produce as output?

From cppreference:

When reading characters, std::istream_iterator skips whitespace by default (unless disabled with std::noskipws or equivalent), while std::istreambuf_iterator does not.

Switching to std::istreambuf_iterator gives the desired behaviour: https://wandbox.org/permlink/RRt8kvyqyvbO1p8m


Why doesn't 123456 as input produce an error?

If there is no space in the input, find_if will return its 2nd argument, which is the end-of-stream iterator.

Dereferencing the end-of-stream iterator is undefined behaviour, which is not guaranteed to produce an error. In fact it doesn't guarantee anything, which means your program might just print the last character in your input.

Mark
  • 1,016
  • 6
  • 10
  • Thanks! Can I ask you why your output is 0 though? On my machine it is blank as expected. Is this an implementation-dependent thing? Also, since std::istream_iterator skips whitespace, shouldn't my version just produce an error or whatever dereferencing an out-of-range iterator gives? – John Aug 23 '19 at 13:42
  • The 0 is not the output but the value returned from `main()`. If you select the output, you can see that there's a line with a single space between Start and 0. Dereferencing a past-the-end iterator is _undefined behaviour_, which means that your program no longer behaves reasonably. And in its unreasonableness, it might just print the last character in your input; or it might crash; or do something else entirely. – Mark Aug 23 '19 at 13:58
  • I see! Thank you for the detailed explanation! – John Aug 23 '19 at 14:03