带有copy_n()和朋友的std :: istream_iterator <>

wil*_*ell 26 c++ istream-iterator

下面的片段从中读取三个整数std::cin; 它写入两个numbers并丢弃第三个:

std::vector<int> numbers(2);
copy_n(std::istream_iterator<int>(std::cin), 2, numbers.begin());
Run Code Online (Sandbox Code Playgroud)

我希望代码能够准确地读取两个整数std::cin,但事实证明这是一个正确的,符合标准的行为.这是标准中的疏忽吗?这种行为的理由是什么?


从C++ 03标准中的24.5.1/1开始:

在构造之后,每次使用++时,迭代器都会读取并存储一个值T.

所以在上面的代码中,在调用时,流迭代器已经读取了一个整数.从那时起,算法中迭代器的每次读取都是预读,从而产生从先前读取缓存的值.

下一个标准的最新草案n3225似乎没有任何变化(24.6.1/1).

在相关的说明中,参考istream_iterator(istream_type& s)构造函数的当前标准的24.5.1.1/2 读取

效果:初始化in_streams.value可以在施工期间或第一次参考时初始化.

重点是" value 可以初始化......"而不是" 应该初始化".这听起来与24.5.1/1相矛盾,但也许这应该是一个自己的问题.

dex*_*ack 10

不幸的是,copy_n的实现者未能解释复制循环中的预读.Visual C++实现在stringstream和std :: cin上都能正常工作.我还检查了原始示例中的情况,其中istream_iterator是在行中构造的.

这是STL实现的关键代码.

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);
    }
Run Code Online (Sandbox Code Playgroud)

这是测试代码

#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
*/
Run Code Online (Sandbox Code Playgroud)


BЈо*_*вић 5

今天我遇到了非常相似的问题,这是示例:

#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 );
}
Run Code Online (Sandbox Code Playgroud)

使用 g++ 4.6.3 编译上面的示例会生成一个:

1.1 2.2 3.3 7 6 3 2 1 1
Run Code Online (Sandbox Code Playgroud)

,并用 g++ 4.7.2 编译产生另一个结果:

1.1 2.2 3.3 8 7 6 3 2 1
Run Code Online (Sandbox Code Playgroud)

c++11 标准讲述了这一点copy_n

template<class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n(InputIterator first, Size n, OutputIterator result);
Run Code Online (Sandbox Code Playgroud)

效果:对于每个非负整数 i < n,执行 *(result + i) = *(first + i)。
返回:结果+n。
复杂性:正好有 n 个作业。

正如您所看到的,没有指定迭代器到底发生了什么,这意味着它依赖于实现。

我的观点是,您的示例不应读取第三个值,这意味着这是标准中的一个小缺陷,因为他们没有指定行为。