2011-06-23 28 views
13

据我所知,迭代器类的层次结构是这样的:可以在需要转发迭代器的情况下使用输入迭代器吗?

Random access -> Bi-directional -> Forward -> Input 
              -> Output 

正确?

我一直认为有一条规则,如果一个算法需要一个特定类型的迭代器,您可以提供链上的类别迭代器,但不能关闭。所以我正在阅读this answer,其中ildjarn 建议 建议(然后稍后更正自己)使用std::ifstreamstd::istream_iteratorstd::search找到文件中的数据。我正要发表评论,你不能这样做,因为search期望转发迭代器,而istream_iterator是一个输入迭代器。但只是为了确保,我试过了:

std::istringstream iss("Elephant hats for sale."); 
std::istream_iterator<char> begin(iss), end; 

std::string sub("hat"); 
auto i = std::search(begin, end, sub.begin(), sub.end()); 

我没想到它会编译,但它确实。但是,结果似乎是无用的,因为如果我遵循它:

while(i != end) 
{ 
    std::cout << *i; 
    ++i; 
} 

没有输出。所以,我的问题是这样的:我的编译器是否允许我使用istream_iterator拨打search?还是没有规则阻止这种事情?

+0

因为我明显的错误; - ]我更新了我的答案,以链接到包装(来自Boost.Spirit)的输入迭代器,使它们适合用作前向迭代器。 – ildjarn

回答

17

可以在需要转发迭代器的地方使用输入迭代器吗?

不。输入迭代器和前向迭代器之间的区别在于输入迭代器是“单遍”迭代器,但前向迭代器是“多遍”迭代器。

一旦你推进了一个输入迭代器,就不能再访问该范围内的前面的元素。如果你创建了一个输入迭代器的副本,那么这两个迭代器都会保持有效直到你推进其中的一个;那么另一个不再有效。

使用前向迭代器,您可以多次迭代序列,您可以同时拥有多个可用的迭代器副本,您可以同时使用多个迭代器到序列中,并且可以解引用迭代器在重新推进前多次重复。

所以,我的问题是这样的:我的编译器在允许我的电话搜索使用istream_iterator

没有规则说编译器必须拒绝代码。

规则是您必须确保传递该函数所需的正确类型的迭代器。有时候,如果你传递错误类型的迭代器,你会得到一个编译错误。有时程序会编译但不能正确运行。有时候事情似乎会正常工作。如果违反调用函数的要求,结果是不确定的。


通用算法通常通过假设类型参数实际提供符合要求强加给其类型参数的要求。因此,例如,一种仅适用于随机访问迭代器的算法将通过执行仅适用于随机访问迭代器的某些操作(例如it + 1)来“执行”该要求。如果迭代器不支持该操作(此处为operator+(iterator, int)),则代码将无法编译。

问题是,没有办法通过这种方式来区分输入迭代器和转发迭代器:您可以对它们进行增量和取消引用;不同之处在于您可以执行每个操作的次数以及执行这些操作的顺序。因此,像std::search这样的算法将使用*it++it,这对于输入迭代器来说“工作”非常好,至少在代码编译的范围内。

理论上,算法可以使用类模板来确定迭代器是输入迭代器还是前向迭代器;我不知道这是否会被C++语言标准所允许。如果图书馆这样做,你可能会得到一个编译错误代码,这会更好。

+0

所以这是不确定的行为,我应该想到这一点。谢谢詹姆斯。 –

+0

“*我不知道C++语言标准是否允许这样做。”为什么不这样做?是不是拒绝编译UB的有效表现? – ildjarn

+2

@ildjarn:我不知道是否需要某个类型具有正确的'iterator_category' typedef才能满足迭代器概念。我认为_would_是必需的,但是libstdC++和VC++实现都没有利用这一点,所以我不确定。 –