【问题标题】:Why is reading from an istream_iterator assigned to a variable not working?为什么从分配给变量的 istream_iterator 读取不起作用?
【发布时间】:2020-10-22 04:39:54
【问题描述】:

我编写了以下程序,它从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), 
                3, 
                std::ostream_iterator<int>(std::cout, " "));              
}

对于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, 3, outs);
    std::copy_n(ins, 3, outs);
}

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

我不明白输出。这是怎么回事,我做错了什么?

另外,请注意,只有在我使用 ins 时才重要。我是否使用outs 不影响输出。

【问题讨论】:

    标签: c++ iterator istream-iterator


    【解决方案1】:

    this reference:

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

    所以,当您第一次创建 ins 变量时,它会立即从 cin 读取 1 并缓存它。

    如果你看copy_n()的声明,输入迭代器是按值传递的,这意味着它是复制的

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

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

    template< class InputIt, class Size, class OutputIt>
    OutputIt copy_n(InputIt first, Size count, 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,它从cin 读取2 并缓存它,然后取消引用first 以接收2 并输出它,然后递增first,它从@987654346 读取@987654345 和@缓存它,然后取消引用first 以接收3 并输出它,然后退出。

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

    【讨论】:

    • 这很清楚,谢谢。我不知道这种缓存行为。这就是让我绊倒的原因,即一旦阅读了1,怎么能再次阅读它?
    【解决方案2】:

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

    copy_n 接受其参数按值,因此第一次调用不会将main()s ins 变量移过它已经读取的1,并且再次提供给第二次调用copy_n

    如果你想要简洁,你可以这样做:

    auto mkins() = [] { return std::istream_iterator<int>(std::cin); }
    
    std::copy_n(mkins(), 3, outs);
    std::copy_n(mkins(), 3, outs);
    

    【讨论】:

    • 谢谢,修复似乎有效。但我不清楚原始代码不起作用的原因。输出是否未指定?
    • @cigien 问题不在输出中。这是因为您为两个 copy_n 调用重用了相同的输入迭代器对象。 Tony 的答案是使用 2 个输入对象,与您的“工作”代码相同。看我的回答,我把问题解释的更详细了。
    • @cigien:我认为“第一次读取istream_iterator 应该由构造函数执行”...?您的代码在完成从第一个迭代器复制之前构造了两个 istream_iterators。
    • 我对 1 在输出中出现两次感到困惑。第一个值似乎被缓存了,我没有从你的回答中意识到这一点。
    猜你喜欢
    • 2016-04-22
    • 1970-01-01
    • 2021-02-22
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 2022-10-05
    • 2017-08-13
    相关资源
    最近更新 更多