I tried to copy the input line into multiple vectors:
#include <vector>
#include <sstream>
#include <istream>
#include <iterator>
#include <algorithm>
#include <iostream>
int main(){
std::vector<int> v1, v2, v3;
std::istringstream is ("1 2 3 4 5 6");
std::istream_iterator<int> iit (is);
std::copy_n(iit, 2, std::back_inserter(v1));
std::copy_n(iit, 2, std::back_inserter(v2));
std::copy(iit, std::istream_iterator<int>(), std::back_inserter(v3));
std::ostream_iterator<int> oit(std::cout, ", ");
std::copy(v1.begin(),v1.end(), oit);
std::cout << "\n";
std::copy(v2.begin(),v2.end(), oit);
std::cout << "\n";
std::copy(v3.begin(),v3.end(), oit);
std::cout << "\n";
return 0;
}
I assume this porgram output:
1, 2,
3, 4,
5, 6,
But I get this:
1, 2,
1, 3,
1, 4, 5, 6,
Why copy_n always insert 1 at the beginning of vectors?
This comes down to a perhaps unintuitive fact of
istream_iterator: it doesn't read when you dereference it, but instead when you advance (or construct) it.We still expect the data to be provided to us via
*it. So, to make this work, each bit of read data has to be temporarily stored in the iterator itself until we next do*it.So, when you create
iit, it's already pulling the first number out for you,1. That data is stored in the iterator. The next available data in the stream is2, which you then pull out usingcopy_n. In total that's two pieces of information delivered, out of a total of two that you asked for, so the firstcopy_nis done.The next time, you're using a copy of
iitin the state it was in before the firstcopy_n. So, although the stream is ready to give you3, you still have a copy of that1"stuck" in your copied stream iterator.Why do stream iterators work this way? Because you cannot detect EOF on a stream until you've tried and failed to obtain more data. If it didn't work this way, you'd have to do a dereference first to trigger this detection, and then what should the result be if we've reached EOF?
Furthermore, we expect that any dereference operation produces an immediate result; with a container that's a given, but with streams you could otherwise be blocking waiting for data to become available. It makes more logical sense to do this blocking on the construction/increment, instead, so that your iterator is always either valid, or it isn't.
If you sack off the copies, and construct a fresh stream iterator for each
copy_n, you should be fine. Though I would generally recommend only using one stream iterator per stream, as that'll avoid anyone having to worry about this.