我编写了以下程序,从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
的输入,程序打印预期的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
,程序打印1 2 3 1 4 5
。
我不明白输出。这是怎么回事,我做错了什么?
另外,请注意,只有当我使用ins
时才重要。无论我是否使用outs
都不会影响输出。
根据此参考:
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
从cin
读取3
并缓存它,然后取消引用first
以接收3
并输出它,然后退出。
再次将ins
传递给copy_n()
时,原始缓存1
仍处于ins
中,并复制到first
参数中。当copy_n()
取消引用first
时,它接收缓存的1
并输出到result
。然后copy_n()
递增first
从cin
读取4
并缓存它,然后取消引用first
以接收4
并输出它,然后递增first
从cin
读取5
并缓存它,然后取消引用first
以接收5
并输出它,然后退出。
如果您查看缺陷报告P0738R2,您将看到istream_iterator
的第一次读取应由构造函数执行,因此它在auto ins = ...
行读取1
。
copy_n
按值获取其参数,因此第一次调用不会将main()
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);