为什么从分配给变量的istream_iterator读取无效?

问题描述

我编写了以下程序,该程序从std::cin读取3个数字,并将它们输出std::cout,并执行两次:

#include <iostream>
#include <algorithm>
#include <iterator>

int main()
{
    std::copy_n(std::istream_iterator<int>(std::cin),3,std::ostream_iterator<int>(std::cout," "));
    
    std::copy_n(std::istream_iterator<int>(std::cin)," "));              
}

对于1 2 3 4 5 6的输入,程序prints是预期的1 2 3 4 5 6


当我发现代码有点冗长时,我试图将迭代器存储在变量中:

#include <iostream>
#include <algorithm>
#include <iterator>

int main()
{
    auto ins = std::istream_iterator<int>(std::cin);
    auto outs = std::ostream_iterator<int>(std::cout," ");
               
    std::copy_n(ins,outs);
    std::copy_n(ins,outs);
}

但是现在对于输入1 2 3 4 5 6,程序prints 1 2 3 1 4 5

我不理解输出。这是怎么回事,我在做什么错了?

此外,请注意,仅当我使用ins时才重要。是否使用outs不会影响输出

解决方法

this reference

std :: istream_iterator是单次输入迭代器,它通过调用适当的运算符>>从为其构造了std :: basic_istream对象中读取类型T的连续对象。实际的读取操作在迭代器增加时执行,而不是在取消引用时执行。构造迭代器时,将读取第一个对象。取消引用仅返回最近读取的对象的副本。

因此,当您首次创建ins变量时,它将立即从1中读取cin并将其缓存。

如果您查看copy_n()的声明,则输入迭代器将通过值传递,这意味着它已被复制

template< class InputIt,class Size,class OutputIt >
OutputIt copy_n( InputIt first,Size count,OutputIt result );

为了论证,我们假设正在使用以下copy_n()的实现(请检查编译器的实际实现):

template< class InputIt,class OutputIt>
OutputIt copy_n(InputIt first,OutputIt result)
{
    if (count > 0) {
        *result++ = *first;
        for (Size i = 1; i < count; ++i) {
            *result++ = *++first;
        }
    }
    return result;
}

当您将ins传递给copy_n()时,该缓存的1将被复制到first参数中。当copy_n()取消引用first时,它将接收缓存的1并输出到result。然后copy_n()递增first,从2读取cin并将其缓存,然后取消引用first以接收2并输出,然后递增{ {1}}从first读取3并将其缓存,然后取消引用cin以接收first并输出它,然后退出。

再次将3传递给ins时,原始缓存的copy_n()仍在1中,并被复制到ins参数中。当first取消引用copy_n()时,它将接收缓存的first并输出到1。然后result递增copy_n(),从first读取4并将其缓存,然后取消引用cin以接收first并输出,然后递增{ {1}}从4读取first并将其缓存,然后取消引用5以接收cin并输出它,然后退出。

,

如果您查看Defect Report P0738R2,将看到istream_iterator的第一次读取应由构造函数执行,因此它在1行读取auto ins = ...

copy_n按值接受,因此第一次调用不会将main()的{​​{1}}变量移过它具有的ins已经阅读,并且再次提供给1的第二次调用。

如果您想简洁,可以执行以下操作:

copy_n