我希望以下内容使buf_iter
指向开始点之后的字符n
。相反,它指向最后读取的字符。为什么会这样?即,如果我在copy_n之前和之后执行in_stream.tellg(),它们的差异不是n
,而是(n-1)
。如果我用in_stream.read
读取n
字符,那么位置将提前n
。
std::istreambuf_iterator<char> buf_iter(in_stream);
std::copy_n(buf_iter, n, sym.begin());
我看过实现,它显然是故意这么做的,跳过了最后的增量。
这里的另一篇文章提到,当from迭代器连接到cin
时,增加它将导致过多的读取,因为读取是在operator++()
上完成的。这听起来像是cin
的问题——为什么operator*()
没有完成读取?
标准在任何地方都有规定吗?我看到的文档没有提到from迭代器发生了什么,我看到了两个不同的页面,它们给出了实现每种行为的"可能的正确实现":
在cppreference,我们有:
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;
}
而在cplusplus.com,我们有:
template<class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n (InputIterator first, Size n, OutputIterator result)
{
while (n>0) {
*result = *first;
++result; ++first;
--n;
}
return result;
}
两者都进行n次读取并在结果中产生相同的内容。但是,第一个迭代器只会将"第一个"迭代器n-1
增加一倍,第二个则会将其增加n
倍。
什么东西?如何编写可移植代码?我可以使用tellg
,然后使用seekg
,但我也可以手动循环(啊!)。
请注意,我并不是在调用copy_n
之后尝试从迭代器中读取,而是在调用copy_n
之后从底层流中读取,问题是copy_n
的字节比我预期的要短
auto pos = in_stream.tellg();
std::istreambuf_iterator<char> buf_iter(in_stream);
std::copy_n(buf_iter, cl, sym.begin());
in_stream.seekg(pos + cl);
uint64_t foo;
in_stream.read(reinterpret_cast<char *>(&foo), 8);
顺便说一句,如果不清楚的话,我会尽量避免将数据复制到缓冲区中,然后再复制到字符串sym
中。
@DaveS:摆脱我的具体问题,这里有一个简单的程序,由于输入迭代器最后一次没有递增,它没有输出我期望的结果:
#include <algorithm>
#include <string>
#include <iostream>
#include <fstream>
int main(int argc, const char * argv[])
{
std::ifstream in("numbers.txt");
std::istreambuf_iterator<char> in_iter(in);
std::ostreambuf_iterator<char> out_iter(std::cout);
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
return 0;
}
输入文件只是"0123456789n"
我得到:
012
234
456
由于istreambuf_iterator::operator++()
的副作用,如果copy_n
被实现为将输入迭代器n
增加一倍,这将给出不同的结果。
@aschepler:需要捕获本地参数,但我会使用它:
std::generate_n(sym.begin(), cl, [&in_stream](){ return in_stream.get(); });
许多std::copy_n
实现增加n-1倍的原因是由于与istream_iterator
的交互,以及通常是如何实现的。
例如,如果您有一个包含整数的输入文件
std::vector<int> buffer(2);
std::istream_iterator<int> itr(stream); // Assume that stream is an ifstream of the file
std::copy_n(itr, 2, buffer.begin());
因为istream_iterator
被指定为递增读取(以及构造或第一次解引用),所以如果std::copy_n
将输入迭代器递增2次,那么实际上会从文件中读取3个值。当copy_n
内部的本地迭代器超出范围时,第三个值将被丢弃。
istreambuf_iterator
没有相同的交互,因为它实际上并没有像大多数istream_iterators
那样将流中的值复制到本地副本中,但copy_n
仍然是这样。
编辑:如果copy-N增加N次,则丢失数据的示例(cplusplus.com的描述似乎不正确)。注意,这实际上只适用于istream_iterators
或其他在递增时读取和删除其底层数据的迭代器。
std::istream_iterator<int> itr(stream); // Reads 1st value
while(n > 0) // N = 2 loop start
{
*result = *first;
++result; ++first; // Reads 2nd value
--n; // N: 1
// N = 1 loop start
*result = *first;
++result; ++first; // Reads 3rd value
--n; // N :0
// Loop exit
}
return result;
n3797[算法.通用]/12
在算法的描述中,运算符
+
和-
用于一些迭代器类别,它们不必被定义。在这些情况下,a+n
的语义与的语义相同X tmp = a; advance(tmp, n); return tmp;
CCD_ 36与相同
return distance(a, b);
[alg.修改.操作]
template<class InputIterator, class Size, class OutputIterator> OutputIterator copy_n(InputIterator first, Size n, OutputIterator result);
5效果:对于每个非负整数i<n,执行CCD_ 37。
6返回:
result + n
。7复杂性:完全是
n
赋值。
我不确定InputIterator(没有多路径)的格式是否正确,因为它不会修改原始迭代器,但总是推进原始迭代程序的副本。它似乎也没有效率。
[input.iterators]/表107-输入迭代器要求(除迭代器外)
表达式:
++r
退货类型:X&
pre:r
不可引用
post:r
是可取消引用的,或者r
已经过了末尾
post:r
以前值的任何副本都不再需要可取消引用或在CCD_ 46的域中。
据我所见,中的a
X tmp = a; advance(tmp, n); return tmp;
因此不再需要是可递增的。
相关缺陷报告:LWG 2173
源迭代器未被引用。因此,它的副本会增加n次,但参数保持不变。
10次中有9次,这就是你想要的。
就具体在input迭代器上递增的副作用而言,我认为官方认为,每次读取时输入迭代器都应该"递增"(不递增的重复读取不会产生相同的值)。所以,只需将增量设为no-op。