Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can input iterators be used where forward iterators are expected?

Tags:

c++

iterator

stl

To my knowledge, the hierarchy of iterator categories goes like this:

Random access -> Bi-directional -> Forward -> Input
                                           -> Output

Correct?

I always thought there was a rule, that if an algorithm expects a particular type of iterator, you can provide iterators of categories up the chain, but not down. So I was reading this answer, where ildjarn suggests suggested (then later corrected himself) using std::ifstream with std::istream_iterator and std::search to find data in a in a file. I was about to comment that you can't do that, because search expects Forward iterators, and istream_iterator is an Input iterator. But just to make sure, I tried this:

std::istringstream iss("Elephant hats for sale.");
std::istream_iterator<char> begin(iss), end;

std::string sub("hat");
auto i = std::search(begin, end, sub.begin(), sub.end());

I didn't expect it to compile, but it did. However, the results seem to be useless because if I follow it with this:

while(i != end)
{
    std::cout << *i;
    ++i;
}

There is no output. So, my question is this: Is my compiler in error for allowing my call to search using istream_iterator? Or are there no rules preventing this sort of thing?

like image 259
Benjamin Lindley Avatar asked Jun 23 '11 04:06

Benjamin Lindley


1 Answers

Can input iterators be used where forward iterators are expected?

No. The difference between an input iterator and a forward iterator is that an input iterator is a "single-pass" iterator but a forward iterator is a "multi-pass" iterator.

Once you advance an input iterator, you can no longer access the previous elements in the range. If you make a copy of an input iterator, both iterators remain valid until you advance one of them; then the other ceases to be valid.

With a forward iterator, you can iterate over the sequence any number of times, you can have multiple usable copies of an iterator at once, you can use multiple iterators into the sequence at the same time, and you can dereference an iterator as many times as you'd like before advancing it again.

So, my question is this: Is my compiler in error for allowing my call to search using istream_iterator?

There is no rule that the compiler must reject the code.

The rule is that you must be sure to pass the right type of iterator that is required by the function. Sometimes if you pass the wrong type of iterator you get a compilation error. Sometimes the program will compile but will not function correctly. Sometimes things will appear to work correctly. The results are undefined if you violate the requirements of calling the function.


Generic algorithms usually impose requirements on their type parameters by assuming that the type arguments provided actually meet the requirements. So, for example, an algorithm that only works with random access iterators will "enforce" this requirement by performing some operation that only works with random access iterators (e.g. it + 1). If the iterator doesn't support that operation (operator+(iterator, int) here), the code will fail to compile.

The problem is that there is no way to distinguish between input iterators and forward iterators this way: you can increment and dereference both of them; the difference is in how many times you can perform each of those operations and the sequence in which you can perform those operations. So, an algorithm like std::search will use *it and ++it, which will "work" just fine for input iterators, at least insofar as the code will compile.

In theory, an algorithm could use the std::iterator_traits class template to determine whether an iterator is an input iterator or a forward iterator; I don't know whether that would be permitted by the C++ language standard. If the library did that, you could get a compilation error for your code, which would be better.

like image 185
James McNellis Avatar answered Oct 22 '22 23:10

James McNellis