比较不同容器的迭代器

comparing iterators from different containers

提问人:fredoverflow 提问时间:1/11/2011 更新时间:1/10/2019 访问量:12393

问:

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

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

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

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

C++ STL 比较 迭代器

评论

0赞 jweyrich 1/12/2011
相关问题:stackoverflow.com/questions/844768/...

答:

2赞 MSalters 1/11/2011 #1

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

评论

0赞 fredoverflow 1/11/2011
所以比较任意指针是非法的吗?我以为这只适用于减法指针。
1赞 fredoverflow 1/11/2011
@MSalters:你是说,对吧?(虽然你在 plaint ints 上的例子也是非法的,但出于不同的原因。int a, b; &a == &b;
3赞 Konrad Rudolph 1/11/2011
@MSalters:我不相信。否则,C++将不允许任何方式具有引用相等性,这在面向对象的代码中很重要,并且大多数实现都会被破坏(测试自赋值)。的确,指针指向数组范围之外(或数组后面)是非法的,但这是不同的。operator =
2赞 Matthieu M. 1/11/2011
@MSalters:正如@jweyrich所指出的,从 ForwardIterator 开始,只有当迭代器属于同一序列时,才有意义。甚至没有为未初始化的迭代器提供。
1赞 fredoverflow 1/13/2011
@eq-:我不认为是非法的,请参阅康拉德的评论。但是,因为读取未初始化的变量会产生未定义的行为。&a == &ba == b
3赞 Blair Holloway 1/11/2011 #2

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

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

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

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

如果没有更多的背景信息,就很难判断这是否能解决你的问题。

评论

0赞 fredoverflow 1/11/2011
你说的“你不能”到底是什么意思?它产生假的?它不编译?这是未定义的行为?无法实施?这没有意义吗?...
1赞 Matthieu M. 1/11/2011
你的意思是标准不允许还是荒谬?
0赞 Blair Holloway 1/12/2011
@Matthieu - 我的意思是荒谬的;我以为我在第二句话中就明白了!
0赞 Blair Holloway 1/12/2011
如果我简单地删除“你不能直接比较来自不同容器的迭代器”,也许我的答案会更好读起来?
0赞 Matthieu M. 1/12/2011
实际上,根据 Jweyrich 的 Perl of Wisdom,除了 InputIterator 和 OutputIterator 之外,它是 C++0x 中未定义的行为
0赞 Mahesh 1/11/2011 #3

ISO/IEC 14882:2003(E) 5.10.1 标准

==(等于)和 !=(不等于)运算符与关系运算符具有相同的语义限制、转换和结果类型,只是它们的优先级和真值结果较低。[ .. ]可以比较指向相同类型的对象或函数的指针(指针转换后)是否相等。当且仅当两个相同类型的指针都为 null、都指向同一函数或都表示相同的地址 (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()

评论

5赞 fredoverflow 1/11/2011
仅仅因为它适用于特殊情况(指针)并不意味着它由一般情况(迭代器)保证。
0赞 Mahesh 1/11/2011
@Konrad Rudolph - 迭代器似乎在指针算术上工作.那么,迭代器不能和指针相提并论吗?
3赞 fredoverflow 1/11/2011
每个指针都是一个迭代器,但反之则不然。例如,不支持“指针算术”。std::list<T>::iterator
0赞 Mahesh 1/11/2011
@FredOverflow - .谢谢。but need not be the other way around
0赞 Mahesh 1/11/2011
stackoverflow.com/questions/2661053/......我读了这篇文章,认为迭代器是一个 c 类型的指针。
0赞 etarion 1/11/2011 #4

我没有从标准的 100% 中得到对输入迭代器的要求,但从那里开始(正向/双向/随机访问迭代器)对 == 的域没有要求,因此它必须在等价关系中返回错误结果。但是,您不能对来自不同容器的迭代器进行<、>或减法。

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

评论

2赞 jweyrich 1/11/2011
The domain of == for forward iterators is that of iterators over the same underlying sequence.§ 24.2.5 (C++0x)
0赞 CB Bailey 1/11/2011
C++03:我认为对域的简化要求仅适用于输入迭代器。 对于输入迭代器,需要是其域上的等价关系,但对于前向迭代器及以上版本,需要是等价关系......句点。======
0赞 etarion 1/11/2011
是的,我指的是C++03,我不知道有0x草案。
1赞 Matthieu M. 1/11/2011
@jweyrich:我认为这值得回答:)
0赞 etarion 1/11/2011
@Matthieu M.:不完全是,它来自一个尚未有效的标准。而当前的没有任何这样的要求,在 OP 的情况下,它也是随机访问迭代器。
4赞 ds27680 1/11/2011 #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++ 标准库活动问题列表“446。不同容器之间的迭代器相等”。也许有人可以检查标准,看看是否采用了更改?

可能不是,因为它在活动问题列表中,所以查尔斯·贝利(Charles Bailey)也回答了这个问题,这是对的,这是未指明的行为。

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

事实上,在 VS 检查附带的 STL 实现中启用迭代器调试后,对于这种确切的情况(来自不同容器的迭代器)来说,至少对我来说,至少再次表明,应尽可能避免进行此类比较。

评论

0赞 Lightness Races in Orbit 10/5/2018
FWIW,这个问题后来得到了解决......通过使原来的措辞更加明确 😂
2赞 CB Bailey 1/11/2011 #6

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

== 是等价关系

请注意,这是对类型的要求,因此必须(在本例中)适用于任何一对有效的(可取消引用或其他方式)。我相信这意味着必须给你一个/答案,不能引起UB。std::vector::iterator==truefalse

— 如果 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()

另请注意,这与 . 用 来定义,其中 和 都是随机访问迭代器。执行的前提条件是必须有一个值,该值要求 和 成为相同范围内的迭代器。<<b - aabb - aDistancena + n == bab

评论

0赞 Matthieu M. 1/11/2011
我相信“如果 a 和 b 都是可取消引用的,那么 a == b 当且仅当 *a 和 *b 是同一个对象”中有一个错别字。我会说如果 THEN ,但反之则在一般情况下不成立。a == b*a == *b
1赞 CB Bailey 1/11/2011
@Matthieu M.:这直接来自标准。注意:“是同一个对象”不是。*a == *b
39赞 jweyrich 1/12/2011 #7

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

§ 24.2.1 — [迭代器.要求.general#6]

当且仅当表达式存在有限的应用序列时,迭代器才被称为可从迭代器访问。如果可从 到达,则它们引用相同序列的元素。ji++ii == jji

§ 24.2.5 — [forward.iterators#2]

对于正向迭代器的域是同一基础序列上的迭代器域。==

鉴于 必须满足 施加的所有要求,则比较来自不同容器的迭代器是不确定的。RandomAccessIteratorForwardIterator

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

除非另有明确说明,否则直接或间接计算任何比较函数或二进制运算符的结果,该运算符具有两个迭代器值作为参数,这些参数是从两个不同的范围 r1 和 r2(包括它们的结束值)获得的,这些值不是一个公共范围的子范围

评论

5赞 Matthieu M. 1/12/2011
+1 观察各种编译器的行为从来都不是权威的,只有(神圣的)标准才应该被依赖,至少C++0对此是精确的。
3赞 Lightness Races in Orbit 10/5/2018
在 C++17 中仍然如此(请参阅 open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#446 了解原因)
0赞 jweyrich 10/6/2018
很棒的添加@LightnessRacesinOrbit!我确实更新了答案以提及它。谢谢。

上一个:C++ 继承问题

下一个:make_unique完美转发