为什么 std::istream_iterator<> 有多个 copy_n() 总是写第一个值

Why std::istream_iterator<> with multiple copy_n() always writes firs value

我试图将输入行复制到多个向量中:

#include <vector>
#include <sstream>
#include <istream>
#include <iterator>
#include <algorithm>
#include <iostream>

int main(){
  std::vector<int> v1, v2, v3;
  std::istringstream is ("1 2 3 4 5 6");
  std::istream_iterator<int> iit (is);
  std::copy_n(iit, 2, std::back_inserter(v1));
  std::copy_n(iit, 2, std::back_inserter(v2));
  std::copy(iit, std::istream_iterator<int>(), std::back_inserter(v3));
  std::ostream_iterator<int> oit(std::cout, ", ");
  std::copy(v1.begin(),v1.end(), oit);
  std::cout << "\n";
  std::copy(v2.begin(),v2.end(), oit);
  std::cout << "\n";
  std::copy(v3.begin(),v3.end(), oit);
  std::cout << "\n";
  return 0;

}

我假设这个程序输出:

1, 2, 
3, 4, 
5, 6,

但我明白了:

1, 2, 
1, 3, 
1, 4, 5, 6, 

为什么copy_n总是在向量的开头插入1?

这归结为 istream_iterator 的一个可能不直观的事实:当您取消引用它时它不会读取,而是 when you advance (or construct) it.


(x indicates a read)

Normal forward iterators:

   Data:            1   2   3   (EOF)
   
   Construction
   *it              x
   ++it
   *it                  x
   ++it
   *it                      x
   ++it                         (`it` is now the one-past-the-end iterator)
   Destruction

Stream iterators:

   Data:            1   2   3   (EOF)
   
   Construction     x
   *it
   ++it                 x
   *it
   ++it                     x
   *it
   ++it                         (`it` is now the one-past-the-end iterator)
   Destruction

我们仍然希望通过 *it 向我们提供数据。因此,为了使这项工作有效,读取数据的每一位都必须临时存储在迭代器本身中,直到我们接下来执行 *it.

因此,当您创建 iit 时,它已经为您提取了第一个数字 1。该数据存储在 迭代器 中。流中的下一个可用数据是 2,然后您可以使用 copy_n 将其拉出。总共提供了两条信息,在您要求的两条信息中,第一条 copy_n 已完成。

下一次,您将使用第一个 copy_n 之前的状态的 iit 副本。因此,尽管流已准备好为您提供 3,但您仍然在复制的流迭代器中“卡住”了那个 1 的副本。


为什么 做流迭代器work this way?因为在您尝试获取更多数据但失败之前,您无法检测到流上的 EOF。如果这样不行,你必须先做一个取消引用来触发这个检测,然后如果我们到达 EOF,结果应该是什么?

此外,我们希望任何取消引用操作都会立即产生结果;使用给定的容器,但使用流时,您可能会阻塞等待数据可用。相反,在 construction/increment 上执行此阻塞更合乎逻辑,因此您的迭代器始终有效,否则无效。


如果你取消副本,并为每个 copy_n 构造一个新的流迭代器,你应该没问题。虽然我通常建议每个流只使用一个流迭代器,因为这样可以避免任何人为此担心。