std::istream_iterator<> with copy_n() and friends
Unfortunately the implementer of copy_n has failed to account for the read ahead in the copy loop. The Visual C++ implementation works as you expect on both stringstream and std::cin. I also checked the case from the original example where the istream_iterator is constructed in line.
Here is the key piece of code from the STL implementation.
template<class _InIt,
class _Diff,
class _OutIt> inline
_OutIt _Copy_n(_InIt _First, _Diff _Count,
_OutIt _Dest, input_iterator_tag)
{ // copy [_First, _First + _Count) to [_Dest, ...), arbitrary input
*_Dest = *_First; // 0 < _Count has been guaranteed
while (0 < --_Count)
*++_Dest = *++_First;
return (++_Dest);
}
Here is the test code
#include <iostream>
#include <istream>
#include <sstream>
#include <vector>
#include <iterator>
int _tmain(int argc, _TCHAR* argv[])
{
std::stringstream ss;
ss << 1 << ' ' << 2 << ' ' << 3 << ' ' << 4 << std::endl;
ss.seekg(0);
std::vector<int> numbers(2);
std::istream_iterator<int> ii(ss);
std::cout << *ii << std::endl; // shows that read ahead happened.
std::copy_n(ii, 2, numbers.begin());
int i = 0;
ss >> i;
std::cout << numbers[0] << ' ' << numbers[1] << ' ' << i << std::endl;
std::istream_iterator<int> ii2(std::cin);
std::cout << *ii2 << std::endl; // shows that read ahead happened.
std::copy_n(ii2, 2, numbers.begin());
std::cin >> i;
std::cout << numbers[0] << ' ' << numbers[1] << ' ' << i << std::endl;
return 0;
}
/* Output
1
1 2 3
4 5 6
4
4 5 6
*/
Today I encountered very similar problem, and here is the example:
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <string>
struct A
{
float a[3];
unsigned short int b[6];
};
void ParseLine( const std::string & line, A & a )
{
std::stringstream ss( line );
std::copy_n( std::istream_iterator<float>( ss ), 3, a.a );
std::copy_n( std::istream_iterator<unsigned short int>( ss ), 6, a.b );
}
void PrintValues( const A & a )
{
for ( int i =0;i<3;++i)
{
std::cout<<a.a[i]<<std::endl;
}
for ( int i =0;i<6;++i)
{
std::cout<<a.b[i]<<std::endl;
}
}
int main()
{
A a;
const std::string line( "1.1 2.2 3.3 8 7 6 3 2 1" );
ParseLine( line, a );
PrintValues( a );
}
Compiling the above example with g++ 4.6.3 produces one:
1.1 2.2 3.3 7 6 3 2 1 1
, and compiling with g++ 4.7.2 produces another result :
1.1 2.2 3.3 8 7 6 3 2 1
The c++11 standard tells this about copy_n
:
template<class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n(InputIterator first, Size n, OutputIterator result);
Effects: For each non-negative integer i < n, performs *(result + i) = *(first + i).
Returns: result + n.
Complexity: Exactly n assignments.
As you can see, it is not specified what exactly happens with the iterators, which means it is implementation dependent.
My opinion is that your example should not read the 3rd value, which means this is a small flaw in the standard that they haven't specified the behavior.