user2393256
user2393256

Reputation: 1150

istream_iterator consumes too much from stream

I get the following input from stdin:

2
5
2 1 5 3 4
5
2 5 1 3 4

The first line represents the number of queues (let's call this value n). Then, for each queue, on the first line there is a value l that is denoting the length of the queue, followed by the actual queue.

I am trying to put the queues in a vector using an istream_iterator as follows:

using namespace std;
int n{};
int l{};
typedef std::istream_iterator<int> input_iterator;
cin >> n;
cout<< "n: " << n << "\n";
for(int i = 0; i < n ; ++i){

    cin >> l;
    cout << "l: " << l << "\n";
    std::vector<int> queue;
    int counter = 0;
    for (input_iterator it(cin); counter < l && it != input_iterator(); ++it){
        queue.push_back((*it));
        ++counter;
    }
    cout<< "Queue: ";
    std::copy(queue.begin(), queue.end(), 
                  std::ostream_iterator<int>(std::cout, " "));
    cout << "\n";
}

This code produces the following output:

n: 2
l: 5
Queue: 2 1 5 3 4 
l: 2
Queue: 5 1 

As you can see the first queue is read correctly. But the second l should be 5, not 2.

What is happening to the 5? Is it consumed by the iterator? Where did i make an error?

Upvotes: 2

Views: 113

Answers (3)

Some programmer dude
Some programmer dude

Reputation: 409166

A for loop is basically just a fancy while loop.

Lets take this for loop from your code:

for (input_iterator it(cin); counter < l && it != input_iterator(); ++i){
    queue.push_back((*it));
    ++counter;
}

It is equivalent to the following:

{
    input_iterator it(cin);
    while (counter < l && it != input_iterator())
    {
        queue.push_back((*it));
        ++counter;
        ++it;
    }
}

Notice that last line in the loop, the ++it; statement? That's what is causing your problem. It will increase the iterator once too many, so after the loop the iterator has read the 5 in the input. The next input operation after the loop will then read the 2 in the next line.

One solution is to keep the iterator, and reuse it inside the outer loop. Perhaps use it for all input.

Another solution, which I hinted about in my comment, is to have for loop from zero to l only and not use an iterator at all but only plain cin >> ....

Upvotes: 2

rocambille
rocambille

Reputation: 15956

Your problem is that your for-loop leaves i in a position next to the last element of the queue. So when calling operator>> to acquire the next value for l, you are one "reading step" too far.

To avoid the problem, you can use the same iterator for all your reading operations -- and rename it to avoid name collision with the variable i in the outer loop, like this:

using namespace std;
int n{};
int l{};
typedef std::istream_iterator<int> input_iterator;
cin >> n;
cout<< "n: " << n << "\n";
input_iterator it(cin);
for(int i = 0; i < n ; ++i){

    l = *(it++);
    cout << "l: " << l << "\n";
    std::vector<int> queue;
    int counter = 0;
    while( counter < l && it != input_iterator() ){
        queue.push_back(*(it++));
        ++counter;
    }
    cout<< "Queue: ";
    std::copy(queue.begin(), queue.end(), 
                  std::ostream_iterator<int>(std::cout, " "));
    cout << "\n";
}

Upvotes: 2

dhavenith
dhavenith

Reputation: 2048

IANALL, but istream_iterator is to my knowledge allowed to read ahead in its operator++(). Since you're re-creating an istream iterator for every group (line of numbers) that you read, you're discarding an iterator that has already read the next integer from the input stream.

One solution would be to create the input iterator only once outside of your for-loop and use it throughout.

Upvotes: 2

Related Questions