【问题标题】:Why is "!=" used with iterators instead of "<"?为什么“!=”与迭代器一起使用而不是“<”?
【发布时间】:2011-10-04 03:56:34
【问题描述】:

我习惯这样写循环:

for (std::size_t index = 0; index < foo.size(); index++)
{
    // Do stuff with foo[index].
}

但是当我在其他人的代码中看到迭代器循环时,它们看起来像这样:

for (Foo::Iterator iterator = foo.begin(); iterator != foo.end(); iterator++)
{
    // Do stuff with *Iterator.
}

我发现iterator != foo.end() 令人反感。如果iterator 增加超过一,也可能很危险。

使用iterator &lt; foo.end() 似乎更“正确”,但我从未在实际代码中看到过。为什么不呢?

【问题讨论】:

  • 使用Iterator &lt;= Foo.End()是不正确的。我想你的意思是Iterator &lt; Foo.End()
  • It can also be dangerous if Iterator is incremented by more than one. end,你已经有未定义的行为。
  • @Maxpm,在 STL 集合中,end() 是序列最后一个对象之后的一个元素。取消引用不是一件有效的事情。这也是为什么您可以安全地使用iter != sequence.end() 而不必担心错过序列的最后一个对象的原因。
  • @Maxpm:我根本不是在谈论取消引用。 增加任何超过end 的迭代器是未定义的行为,无论您是否取消引用它
  • @Maxpm:哪个容器?对于链表来说这很简单,例如,迭代器内只有一个空指针,而不是指向节点的有效指针。对于向量,只在内部分配的数组末尾之后就可以了。

标签: c++ stl iterator comparison-operators


【解决方案1】:

所有迭代器都是相等可比的。只有随机访问迭代器具有关系可比性。输入迭代器、前向迭代器和双向迭代器在关系上没有可比性。

因此,使用!= 的比较比使用&lt; 的比较更通用和灵活。


迭代器有不同的类别,因为并非所有元素范围都具有相同的访问属性。例如,

  • 如果你有一个迭代器进入一个数组(一个连续的元素序列),对它们进行关系比较就很简单了;您只需要比较指向元素的索引(或指向它们的指针,因为迭代器可能只包含指向元素的指针);

  • 1234563迭代器,或者你到达列表的末尾。

规则是迭代器上的所有操作都应该具有恒定的时间复杂度(或者,至少是次线性时间复杂度)。您始终可以在恒定时间内执行相等比较,因为您只需要比较迭代器是否指向同一个对象。所以,所有的迭代器都是相等可比的。


此外,不允许将迭代器递增到其指向的范围的末尾。因此,如果您最终遇到it != foo.end()it &lt; foo.end() 做的事情不同的情况,那么您已经有未定义的行为,因为您已经迭代超出了范围的末尾。

指向数组的指针也是如此:不允许将指针递增到超出数组末尾的位置;这样做的程序表现出未定义的行为。 (索引显然不是这样,因为索引只是整数。)

一些标准库实现(如 Visual C++ 标准库实现)具有有用的调试代码,当您使用这样的迭代器执行非法操作时会引发断言。

【讨论】:

  • +1;作为支持此答案的具体示例,很难在小于线性时间内为std::list&lt;T&gt;::iterator 实现operator&lt;,但operator!= 可以在恒定时间内工作。
  • 你也可能指的是it &lt; foo.end()而不是it &lt;= foo.end()
  • @zneak:哦,是的。非常感谢。
  • 此外,不允许将迭代器递增到其指向的范围的末尾。因此,如果您最终遇到 it != foo.end() 与 it 即使您不取消对迭代器的引用,而是将其与带有“
  • @khuttun:您总是可以将迭代器推进到“过去的”迭代器,但只能一次。你不能推进一个“过去的”迭代器;导致 UB。
【解决方案2】:

简答:因为Iterator 不是数字,而是对象。

更长的答案:集合比线性数组更多。例如,树和散列并不真正适合“这个索引在另一个索引之前”。例如,对于一棵树,两个索引位于不同的分支上。或者,哈希中的任何两个索引——它们根本没有顺序,所以你对它们施加的任何顺序都是任意的。

您不必担心“丢失”End()。它也不是一个数字,它是一个代表集合结束的对象。有一个越过它的迭代器是没有意义的,事实上它不能。

【讨论】:

  • 但是如果一个结构的元素不能被表示为“之前”或“之后”,那么为它设置一个迭代器是否有意义?毕竟,你无法做到Iterator++,而迭代器的全部意义在于迭代
  • @Maxpm,迭代器对于枚举对象也很有用。作为一个具体的例子,你不能根据自己的喜好来固定 std::set&lt;T&gt; 中元素的顺序,但它仍然是可迭代的。此外,并不是说 不可能 告诉哪个元素先出现。可能是所需的复杂性比预期的要大(使用树示例,判断哪个节点先出现的最坏情况比几次比较要糟糕得多);因此,最好不要实现该功能并让用户陷入可悲的性能陷阱。
  • @Maxpm,当然,每个集合都有一个物理顺序,这是在非量子计算机上在有限空间中表示的必要条件。但是,该顺序是完全任意的,不一定与集合的 逻辑 顺序相关,即使它有一个。因此,即使您可以迭代(通过物理顺序),该顺序也不一定有意义。
  • 这个答案离题了。 @zneak 是对的,问题不在于没有明智的方法在 std::set(毕竟是有序的)这样的集合上实现 operator&lt;,问题在于它需要的复杂性高于常量。
猜你喜欢
  • 2012-10-07
  • 2013-09-01
  • 2018-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-19
相关资源
最近更新 更多