【问题标题】:comparing iterators from different containers比较来自不同容器的迭代器
【发布时间】:2011-06-07 03:35:24
【问题描述】:

比较来自不同容器的迭代器是否合法?

std::vector<int> foo;
std::vector<int> bar;

表达式foo.begin() == bar.begin() 是否产生错误或未定义的行为?

(我正在编写一个自定义迭代器,并在实现operator== 时偶然发现了这个问题。)

【问题讨论】:

标签: c++ stl comparison iterator


【解决方案1】:

没有。如果它是合法的,这意味着指针不会是迭代器。

【讨论】:

  • 那么比较任意指针是非法的?我认为这只适用于减去指针。
  • @MSalters:你的意思是int a, b; &amp;a == &amp;b;,对吧? (尽管您在原告整数上的示例也是非法的,但出于不同的原因。)
  • @MSalters:我不相信。否则 C++ 将不允许任何方式具有引用相等性,这在面向对象的代码中很重要,并且大多数 operator = 实现将被破坏(测试自分配)。确实,指针指向数组范围之外(或后面)是非法的,但这是不同的。
  • @MSalters:正如@jweyrich 指出的那样,从 ForwardIterator 开始,只有在迭代器属于同一序列时才比较它们才有意义。甚至没有提供未初始化的迭代器。
  • @eq-:我不认为&amp;a == &amp;b 是非法的,请参阅 Konrad 的评论。但是,a == b 是,因为读取未初始化的变量会产生未定义的行为。
【解决方案2】:

您不能直接比较来自不同容器的迭代器。迭代器是一个使用容器的内部状态对其进行遍历的对象;将一个容器的内部结构与另一个容器进行比较根本没有意义。

但是,如果从container.begin() 产生的迭代器可用,则可能通过从begin() 遍历的对象数与当前迭代器值进行比较是有意义的。这是使用std::distance 完成的:

int a = std::distance(containerA.begin(), iteratorA);
int b = std::distance(containerB.begin(), iteratorB);

if (a <comparison> b)
{ /* ... */ }

没有更多上下文,很难判断这是否能解决您的问题。 YMMV。

【讨论】:

  • “你不能”到底是什么意思?它产生错误?它不编译?这是未定义的行为?不可能实现?它没有任何意义? ...
  • 你的意思是标准不允许还是无意义?
  • @Matthieu - 我的意思是荒谬的;我以为我在第二句话中澄清了这一点!
  • 如果我简单地删除“你不能直接比较来自不同容器的迭代器”,也许我的答案会更好?
  • 其实根据jweyrich的perl智慧,除了InputIterator和OutputIterator之外,在C++0x中是未定义的行为
【解决方案3】:

ISO/IEC 14882:2003(E) 5.10.1

==(等于)和 !=(不等于)运算符与关系运算符具有相同的语义限制、转换和结果类型,但它们的优先级较低且结果为真值。 [ .. ] 指向相同类型的对象或函数的指针(在指针转换之后)可以进行相等性比较。相同类型的两个指针比较相等当且仅当它们都为空,都指向同一个函数,或者都表示相同的地址(3.9.2)。

XCode (3.2.3) 上的模拟结果:

#include <iostream>
#include <vector>

int main()
{
    std::vector <int> a,aa;
    std::vector <float> b;

    if( a.begin() == aa.begin() )
        std::cout << "\n a.begin() == aa.begin() \n" ;

    a.push_back(10) ;

    if( a.begin() != aa.begin() )
        std::cout << "\n After push back a.begin() != aa.begin() \n" ;

    // Error if( a.begin() == b.begin() )   

    return 0;
}

输出:

a.begin() == aa.begin()
后推 a.begin() != aa.begin()

【讨论】:

  • 仅仅因为它适用于特殊情况(指针)并不意味着它可以由一般情况(迭代器)保证。
  • @Konrad Rudolph - 迭代器似乎在指针算术上工作。那么,不能将迭代器与指针进行比较吗?
  • 每个指针都是一个迭代器,但反之则不然。比如std::list&lt;T&gt;::iterator不支持“指针运算”。
  • @FredOverflow - but need not be the other way around。谢谢。
  • stackoverflow.com/questions/2661053/…我读了这篇文章,认为迭代器是一个c类指针。
【解决方案4】:

我没有从标准的 100% 中得到对输入迭代器的要求,但是从那里(前向/双向/随机访问迭代器)对 == 的域没有要求,所以它必须 return false 导致等价关系。但是,您不能对来自不同容器的迭代器执行 或减法。

编辑:它不必返回 false,它必须产生等价关系,这允许两个空容器的 .begin() 比较相等(如另一个答案所示)。如果迭代器是可解引用的,a == b =&gt; *a == *b 必须持有。它仍然不是未定义的行为。

【讨论】:

  • The domain of == for forward iterators is that of iterators over the same underlying sequence. § 24.2.5 (C++0x)
  • C++03:我认为对== 域的降低要求仅适用于输入迭代器。对于输入迭代器,== 必须是在其域上 的等价关系,但对于前向迭代器及以上的迭代器,== 必须是等价关系...句号。
  • 是的,我指的是 C++03,我不知道有 0x 草稿。
  • @jweyrich:我认为这将保证答案:)
  • @Matthieu M.:不完全是,它来自一个尚未生效的标准。而当前的没有任何这样的要求,在 OP 的情况下它也是随机访问迭代器。
【解决方案5】:

据我所知,未定义的行为。在 VS 2010 中带有

/*
* to disable iterator checking that complains that the iterators are incompatible (come from * different containers :-)
*/
#define _HAS_ITERATOR_DEBUGGING 0 

std::vector<int> vec1, vec2;

std::vector<int>::iterator it1 = vec1.begin();
std::vector<int>::iterator it2 = vec2.begin();

if (it1 == it2)
{
std::cout << "they are equal!!!"; 
}

在这种情况下,相等性测试返回 true :-),因为容器是空的,并且迭代器的 _Ptr 成员都是 nullptr。

谁知道你的实现可能会做不同的事情,测试会返回 false :-)。

编辑:

参见C++ Standard library Active Issues list“446. 不同容器之间的迭代器相等”。也许有人可以检查标准以查看更改是否被采用?

可能不是,因为它在活动问题列表中,所以也回答了这个问题的 Charles Bailey 是正确的,它是未指定的行为。

所以我猜想不同实现之间的行为可能会有所不同(至少在理论上),这只是一个问题。

事实上,在 STL 实现中启用了迭代器调试,并带有 VS 检查,对于这种确切的情况(迭代器来自不同的容器),至少对我来说再次发出信号,应该尽可能避免进行这种比较。

【解决方案6】:

我相信这是未指定的行为 (C++03)。 std::vector 迭代器是随机访问迭代器,== 的行为在前向迭代器的要求中定义。

== 是等价关系

请注意,这是对类型的要求,因此必须(在这种情况下)适用于任何一对有效(可取消引用或其他)std::vector::iterators。我相信这意味着==必须给你一个true/false的答案并且不能引起UB。

——如果 a 和 b 相等,则 a 和 b 都可以取消引用,否则都不能取消引用。

相反,可取消引用的迭代器无法与不可取消引用的迭代器进行比较。

——如果 a 和 b 都是可解引用的,那么当且仅当 *a 和 *b 是同一个对象时 a == b。

请注意,对于两个不可取消引用的迭代器,是否没有a == b 的要求。只要== 是传递的(如果a.end() == b.end()b.end() == c.end() 然后a.end() == c.end()),自反(a.end() == a.end())和对称的(如果a.end() == b.end() 然后b.end() == a.end())它无关紧要如果一些,所有或者没有 end() 不同容器的迭代器比较相等。

另外请注意,这与&lt; 形成对比。 &lt; 是根据b - a 定义的,其中ab 都是随机访问迭代器。执行b - a 的前提条件是必须有一个Distancen 使得a + n == b 要求ab 成为同一范围内的迭代器。

【讨论】:

  • 我相信“如果 a 和 b 都是可取消引用的,那么 a == b 当且仅当 *a 和 *b 是同一个对象”中有一个错字。我会说如果a == b THEN *a == *b,但反过来在一般情况下不成立。
  • @Matthieu M.:这直接来自标准。注意:“是同一个对象”不是*a == *b
【解决方案7】:

如果考虑 C++11 标准 (n3337):

§ 24.2.1 — [iterator.requirements.general#6]

迭代器 j 被称为可从迭代器 i 到达当且仅当表达式 ++i 的应用程序的有限序列使得 i == j。如果从i 可以访问j,则它们指的是相同序列的元素。

§ 24.2.5 — [forward.iterators#2]

前向迭代器的== 的域是相同底层序列上的迭代器的域。

鉴于RandomAccessIterator 必须满足ForwardIterator 提出的所有要求,比较来自不同容器的迭代器是未定义的。

LWG issue #446专门讨论了这个问题,建议在标准中添加以下文字(感谢@Lightness Races in Orbit引起注意):

直接或间接评估任何比较函数或二元运算符的结果,其中两个迭代器值作为参数,这些迭代器值是从两个不同的范围 r1 和 r2(包括它们的过去值)不是一个公共范围的子范围是未定义的,除非另有明确说明

【讨论】:

  • +1 观察各种编译器的行为从来都不是权威的,应该只依赖(神圣的)标准,至少C++0x是精确的。
  • 在 C++17 中仍然如此(请参阅open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#446 了解原因)
  • 优秀的补充@LightnessRacesinOrbit!我确实更新了答案以提及它。谢谢。
猜你喜欢
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 2013-04-29
  • 2012-03-28
  • 2017-05-02
  • 1970-01-01
  • 2018-05-31
相关资源
最近更新 更多