【问题标题】:Can input iterators be used where forward iterators are expected?输入迭代器可以用在需要前向迭代器的地方吗?
【发布时间】:2011-09-20 21:54:24
【问题描述】:

据我所知,迭代器类别的层次结构如下:

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

正确吗?

我一直认为有一个规则,如果一个算法需要一种特定类型的迭代器,你可以在链上提供类别的迭代器,但不能向下。所以我正在阅读this answer,其中 ildjarn suggests 建议(后来更正自己)使用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?还是没有规则阻止这种事情发生?

【问题讨论】:

  • 自从我明显的错误 ;-] 我更新了我的答案,其中包含输入迭代器的包装器(来自 Boost.Spirit)的链接,使它们适合用作前向迭代器。

标签: c++ stl iterator


【解决方案1】:

输入迭代器可以用在需要前向迭代器的地方吗?

没有。输入迭代器和前向迭代器之间的区别在于,输入迭代器是“单程”迭代器,而前向迭代器是“多程”迭代器。

推进输入迭代器后,您将无法再访问该范围内的先前元素。如果您复制输入迭代器,则两个迭代器都将保持有效,直到您推进其中一个;然后另一个不再有效。

使用前向迭代器,您可以对序列进行任意次数的迭代,一次可以拥有多个可用的迭代器副本,可以同时在序列中使用多个迭代器,还可以取消对迭代器的引用在再次推进之前,你想多少次都可以。

所以,我的问题是:我的编译器是否因允许我使用istream_iterator 进行搜索而出错?

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

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


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

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

理论上,算法可以使用std::iterator_traits类模板来确定迭代器是输入迭代器还是前向迭代器;我不知道 C++ 语言标准是否允许这样做。如果库这样做了,您的代码可能会出现编译错误,这样会更好。

【讨论】:

  • 所以这是未定义的行为,我应该想到这一点。谢谢詹姆斯。
  • "我不知道 C++ 语言标准是否允许这样做。" 为什么不允许呢?拒绝编译不是UB的有效体现吗?
  • @ildjarn:我不知道是否需要类型具有正确的iterator_category typedef 才能满足迭代器概念。我认为 是必需的,但是 libstdc++ 和 VC++ 实现都没有利用这一点,所以我不确定。
  • "一旦你取消引用一个输入迭代器,你不能再次取消引用它,直到你推进它(使用++"。 C++11 [24.2.3] 说(对于 InputIterators)“表达式(void)*a,*a 等同于*a”——这似乎表明允许多次取消引用。我没有看到有关您的其他评论的任何提及,“如果您复制输入迭代器,则两个迭代器都有效,直到您前进或取消引用其中一个...... i>" --- 我不明白为什么这个取消引用会使另一个迭代器失效。
  • @nknight:看来您是正确的(我的回答中的陈述不正确)。我今晚会更正它。感谢您提请我注意!
猜你喜欢
  • 2012-12-13
  • 2014-01-20
  • 2011-01-03
  • 2020-01-21
  • 2012-02-10
  • 2018-02-16
  • 2011-07-09
  • 2011-03-26
相关资源
最近更新 更多