【问题标题】:Why std::istream_iterator<> with multiple copy_n() always writes firs value为什么带有多个 copy_n() 的 std::istream_iterator<> 总是写入第一个值
【发布时间】:2020-07-16 17:54:26
【问题描述】:

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

#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;

}

我假设这个 porgram 输出:

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

但我明白了:

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

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

【问题讨论】:

  • 如果您阅读了the first sentence of std::istream_iterator's description,请结合显示的代码将迭代器按值传递给std::copy_n和@ 987654327@ 修改了迭代器,然后显示的代码尝试使用相同的迭代器的原始值,您可以从中得出什么结论?
  • @SamVarshavchik 您说的是:“在构造迭代器时读取第一个对象”?所以我可能每次使用副本时都必须创建一个新的 istream_iterator?

标签: c++ algorithm istream-iterator


【解决方案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 已完成。

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


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

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


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

【讨论】:

  • 因此,当您建议每个流使用一个流迭代器时。这是否意味着每次在副本或其他任何地方使用它时都会创建一个新的迭代器?
  • 其实应该没问题。告诉我进展如何。
  • @FedorGoncharov,一条溪流就像……一条溪流,一条河流。一旦它的一大块过去了,你就不会再看到它了。
  • @EnricoMariaDeAngelis 对于某些类别的流 ;) 但是,将它们视为数据流而不是容器肯定会有所帮助。
  • @AsteroidsWithWings,我指的是我所知道的唯一概念 stream;什么流是可重复的?
猜你喜欢
  • 2011-07-01
  • 1970-01-01
  • 2017-03-13
  • 2018-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-24
  • 2023-03-12
相关资源
最近更新 更多