下面的代码段从std::cin
读取三个整数;它将两个写入numbers
并丢弃第三个:
std::vector<int> numbers(2);
copy_n(std::istream_iterator<int>(std::cin), 2, numbers.begin());
我希望代码能够从std::cin
精确读取两个整数,但事实证明这是一个正确的,符合标准的行为。这是标准中的疏忽吗?这种行为的理由是什么?
C ++ 03标准中的24.5.1 / 1:
构建之后,每一个 使用time ++,迭代器读取 并存储值
T
。
所以在上面的代码中,在调用时,流迭代器已经读取了一个整数。从那时起,算法中迭代器的每次读取都是预读,产生从前一次读取缓存的值。
下一个标准n3225的最新草案似乎没有任何变化(24.6.1 / 1)。
在相关的说明中,参考istream_iterator(istream_type& s)
构造函数的当前标准的24.5.1.1/2读取
效果:使用初始化
in_stream
s
。value
可能会在此期间初始化 建设或第一次 引用。
强调“value
可以初始化......”而不是“ 初始化”。这听起来与24.5.1 / 1相矛盾,但也许这应该是一个自己的问题。
答案 0 :(得分:10)
不幸的是,copy_n的实现者未能解释复制循环中的预读。 Visual C ++实现在stringstream和std :: cin上都能正常工作。我还检查了原始示例中的情况,其中istream_iterator是在行中构造的。
以下是STL实现的关键代码。
template<class _InIt,
class _Diff,
class _OutIt> inline
_OutIt _Copy_n(_InIt _First, _Diff _Count,
_OutIt _Dest, input_iterator_tag)
{ // copy [_First, _First + _Count) to [_Dest, ...), arbitrary input
*_Dest = *_First; // 0 < _Count has been guaranteed
while (0 < --_Count)
*++_Dest = *++_First;
return (++_Dest);
}
这是测试代码
#include <iostream>
#include <istream>
#include <sstream>
#include <vector>
#include <iterator>
int _tmain(int argc, _TCHAR* argv[])
{
std::stringstream ss;
ss << 1 << ' ' << 2 << ' ' << 3 << ' ' << 4 << std::endl;
ss.seekg(0);
std::vector<int> numbers(2);
std::istream_iterator<int> ii(ss);
std::cout << *ii << std::endl; // shows that read ahead happened.
std::copy_n(ii, 2, numbers.begin());
int i = 0;
ss >> i;
std::cout << numbers[0] << ' ' << numbers[1] << ' ' << i << std::endl;
std::istream_iterator<int> ii2(std::cin);
std::cout << *ii2 << std::endl; // shows that read ahead happened.
std::copy_n(ii2, 2, numbers.begin());
std::cin >> i;
std::cout << numbers[0] << ' ' << numbers[1] << ' ' << i << std::endl;
return 0;
}
/* Output
1
1 2 3
4 5 6
4
4 5 6
*/
答案 1 :(得分:3)
今天我遇到了非常类似的问题,这是一个例子:
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <string>
struct A
{
float a[3];
unsigned short int b[6];
};
void ParseLine( const std::string & line, A & a )
{
std::stringstream ss( line );
std::copy_n( std::istream_iterator<float>( ss ), 3, a.a );
std::copy_n( std::istream_iterator<unsigned short int>( ss ), 6, a.b );
}
void PrintValues( const A & a )
{
for ( int i =0;i<3;++i)
{
std::cout<<a.a[i]<<std::endl;
}
for ( int i =0;i<6;++i)
{
std::cout<<a.b[i]<<std::endl;
}
}
int main()
{
A a;
const std::string line( "1.1 2.2 3.3 8 7 6 3 2 1" );
ParseLine( line, a );
PrintValues( a );
}
用g ++ 4.6.3编译上面的例子产生一个:
1.1 2.2 3.3 7 6 3 2 1 1
,用g ++ 4.7.2编译会产生另一个结果:
1.1 2.2 3.3 8 7 6 3 2 1
c ++ 11标准告诉我copy_n
:
template<class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n(InputIterator first, Size n, OutputIterator result);
效果:对于每个非负整数i&lt; n,执行*(结果+ i)= *(第一+ i) 返回:结果+ n。
复杂性:完全是n个任务。
正如您所看到的,没有指定迭代器究竟发生了什么,这意味着它依赖于实现。
我的观点是你的例子不应该读取第3个值,这意味着这是标准中的一个小缺陷,他们没有指定行为。
答案 2 :(得分:1)
我不知道确切的理由,但由于迭代器也必须支持operator *(),因此它必须缓存它读取的值。允许迭代器在构造时缓存第一个值简化了这一点。它还有助于在流最初为空时检测流末尾。
也许您的用例是委员会没有考虑的用例?
答案 3 :(得分:0)
今天,在您之后的9年,我遇到了同样的问题,因此,按照这个主题进行讨论的同时发现这个问题,似乎我们可以在第一次阅读后为每次阅读的内容走一个步骤(我的意思是{{ 1}}也不能自动忽略换行结束,我们借助cin
来帮助它,我想我们也可以帮助该实现):
cin.ignore()
并且应该产生如下输出:
#include<bits/stdc++.h>
using namespace std;
int main(){
freopen("input.txt","r",stdin);
istream_iterator<int> it(cin);
ostream_iterator<int> cout_it(cout, " ");
copy_n(it, 5, cout_it);
cout<<"\nAnd for the rest of the stream\n";
for(int i=0;i<10;i++){
it++;
copy_n(it, 1, cout_it);
}
return 0;
}