为什么 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
构造一个新的流迭代器,你应该没问题。虽然我通常建议每个流只使用一个流迭代器,因为这样可以避免任何人为此担心。
我试图将输入行复制到多个向量中:
#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
构造一个新的流迭代器,你应该没问题。虽然我通常建议每个流只使用一个流迭代器,因为这样可以避免任何人为此担心。