比较来自不同容器的迭代器

时间:2011-01-11 12:18:02

标签: c++ stl comparison iterator

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

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

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

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

7 个答案:

答案 0 :(得分:32)

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

  

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

     

当且仅当存在j表达式的i的有限应用序列时,才会从迭代器++i中调用迭代器i == j。如果j可以从i访问,则它们会引用相同序列的元素。

  

§ 24.2.5 — [forward.iterators#2]

     

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

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

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

  

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

答案 1 :(得分:4)

据我所知,未定义的行为。在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。

谁知道你的实现可能会有不同的做法,测试会返回错误: - )。

编辑:

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

可能不是因为它在活动问题清单上,所以Charles Bailey也回答了这个问题是对的,这是未指明的行为。

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

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

答案 2 :(得分:3)

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

但是,如果container.begin()产生的迭代器可用,那么可能可以通过从begin()遍历的对象数到当前迭代器值来比较迭代器。这是使用std::distance

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

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

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

答案 3 :(得分:2)

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

答案 4 :(得分:2)

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

  

==是等价关系

请注意,这是对类型的要求,因此必须适用于(在这种情况下)任何一对有效(可解除引用或其他)std::vector::iterator。我认为这意味着==必须给您一个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()则{ {1}})如果不同容器的某些,所有或没有b.end() == a.end()迭代器相等,则无关紧要。

另请注意,这与end()形成对比。 <是根据<定义的,其中b - aa都是随机访问迭代器。执行b的前提条件是必须有b - aDistancen需要a + n == ba迭代器进入相同的范围。

答案 5 :(得分:0)

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()

之后

答案 6 :(得分:0)

我没有从标准100%获得对输入迭代器的要求,但是从那里开始(正向/双向/随机访问迭代器)对==的域没有要求,所以它必须返回false 导致等价关系。你做不到&lt;或者&gt;或者减去来自不同容器的迭代器。

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