43

Is it legal to compare iterators from different containers?

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

Does the expression foo.begin() == bar.begin() yield false or undefined behavior?

(I am writing a custom iterator and stumbled upon this question while implementing operator==.)

1

7 Answers 7

39

If you consider the C++11 standard (n3337):

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

An iterator j is called reachable from an iterator i if and only if there is a finite sequence of applications of the expression ++i that makes i == j. If j is reachable from i, they refer to elements of the same sequence.

§ 24.2.5 — [forward.iterators#2]

The domain of == for forward iterators is that of iterators over the same underlying sequence.

Given that RandomAccessIterator must satisfy all requirements imposed by ForwardIterator, comparing iterators from different containers is undefined.

The LWG issue #446 talks specifically about this question, and the proposal was to add the following text to the standard (thanks to @Lightness Races in Orbit for bringing it to attention):

The result of directly or indirectly evaluating any comparison function or the binary - operator with two iterator values as arguments that were obtained from two different ranges r1 and r2 (including their past-the-end values) which are not subranges of one common range is undefined, unless explicitly described otherwise.

Sign up to request clarification or add additional context in comments.

3 Comments

+1 Observing the behavior of various compilers has never been authoritative, only the (holy) standard should be relied upon, and at least C++0x is precise about it.
Excellent addition @LightnessRacesinOrbit! I did update the answer to mention it. Thank you.
4

Undefined behavior as far as I know. In VS 2010 with

/*
* 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!!!"; 
}

The equality test returns in this case true :-), since the containers are empty and the _Ptr member of the iterators are both nullptr.

Who knows maybe your implementation does things differently and the test would return false :-).

EDIT:

See C++ Standard library Active Issues list "446. Iterator equality between different containers". Maybe someone can check the standard to see if the change was adopted?

Probably not since it is on the active issues list so Charles Bailey who also answered this is right it's unspecified behavior.

So I guess the behavior could differ (at least theoretically) between different implementations and this is only one problem.

The fact that with iterator debugging enabled in the STL implementation that comes with VS checks are in place for this exact case (iterators coming from different containers) singnals at least to me once more that doing such comparisons should be avoided whenever possible.

1 Comment

3

You cannot directly compare iterators from different containers. An iterator is an object that uses the internal state of a container to traverse it; comparing the internals of one container to another simply does not make sense.

However, if the iterators resulting from container.begin() are available, it may make sense to compare iterators by the count of objects traversed from begin() to the current iterator value. This is done using std::distance:

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

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

Without more context, it's difficult to judge whether this would solve your problem or not. YMMV.

5 Comments

What exactly do you mean by "You cannot"? It yields false? It does not compile? It is undefined behavior? It's impossible to implement? It does not make sense? ...
do you mean disallowed by the standard or non-sensical ?
@Matthieu - I meant non-sensical; I thought I cleared that up in the second sentence!
Perhaps my answer would read better if I simply removed "You cannot directly compare iterators from different containers" ?
actually, according to jweyrich's perl of wisdom, it is undefined behavior according to the C++0x except for InputIterator and OutputIterator.
2

I believe that it is unspecified behaviour (C++03). std::vector iterators are random access iterators and the behaviour of == is defined in the requirements for forward iterators.

== is an equivalence relation

Note that this is a requirement on a type, so must be applicable (in this case) to any pair of valid (dereferencable or otherwise) std::vector::iterators. I believe that this means == must give you a true/false answer and can't cause UB.

— If a and b are equal, then either a and b are both dereferenceable or else neither is dereferenceable.

Conversely, a dereferenceable iterator cannot compare equal to an iterator that is not dereferenceable.

— If a and b are both dereferenceable, then a == b if and only if *a and *b are the same object.

Note the lack of requirement on whether a == b for two iterators that aren't dereferenceable. So long as == is transitive (if a.end() == b.end() and b.end() == c.end() then a.end() == c.end()), reflexive (a.end() == a.end()) and symmetric (if a.end() == b.end() then b.end() == a.end()) it doesn't matter if some, all or no end() iterators to different containers compare equal.

Note, also, that this is in contrast to <. < is defined in terms of b - a, where a and b are both random access iterators. A pre-condition of performing b - a is that there must be a Distance value n such that a + n == b which requires a and b to be iterators into the same range.

2 Comments

I believe there is a typo in "If a and b are both dereferenceable, then a == b if and only if *a and *b are the same object". I would say that if a == b THEN *a == *b, but the reverse does not hold in the general case.
@Matthieu M.: That comes direct from the standard. Note: "are the same object" not *a == *b.
1

No. If it were legal, this would imply that pointers would not be iterators.

12 Comments

So comparing arbitrary pointers is illegal? I thought that would only apply to subtracting pointers.
@MSalters: You mean int a, b; &a == &b;, right? (Although your example on plaint ints is also illegal, but for a different reason.)
@MSalters: I don’t believe that. Otherwise C++ wouldn’t allow any way to have reference equality, which is kind of important in object oriented code, and most operator = implementations would be broken (test for self-assignment). It’s true that it’s illegal to have pointers that point outside an array’s range (or behind it) but that’s different.
@MSalters: as @jweyrich pointed out, from ForwardIterator and onward, it only makes sense to compare iterators if they belong to the same sequence. There is not even a provision for uninitialized iterators.
@eq-: I don't think &a == &b is illegal, see Konrad's comment. a == b, however, is, because reading an uninitialized variable yields undefined behavior.
|
0

ISO/IEC 14882:2003(E) 5.10.1

The == (equal to) and the != (not equal to) operators have the same semantic restrictions, conversions, and result type as the relational operators except for their lower precedence and truth-value result. [ .. ] Pointers to objects or functions of the same type (after pointer conversions) can be compared for equality. Two pointers of the same type compare equal if and only if they are both null, both point to the same function, or both represent the same address (3.9.2).

Simulation Results on 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;
}

Output :

a.begin() == aa.begin()
After push back a.begin() != aa.begin()

5 Comments

Just because it works for a special case (pointers) does not mean it is guaranteed by the general case (iterators).
@Konrad Rudolph - Iterators seem like they work on pointer arithematic. So, can't iterators be compared to pointers ?
Every pointer is an iterator, but not the other way around. For example, std::list<T>::iterator does not support "pointer arithmetic".
@FredOverflow - but need not be the other way around. Thanks.
stackoverflow.com/questions/2661053/… I read this article and thought iterator is a c kind pointer.
0

I don't get the requirements on input iterators from the standard 100%, but from there on (forward/bidirectional/random access iterators) there are no requirements on the domain of ==, so it must return false result in an equivalence relation. You can't do < or > or subtraction on iterators from different containers though.

Edit: It does not have to return false, it has to result in an equivalence relation, this allows .begin() of two empty containers to compare equal (as shown in another answer). If the iterators are dereferencable, a == b => *a == *b has to hold. It's still not undefined behaviour.

7 Comments

The domain of == for forward iterators is that of iterators over the same underlying sequence. § 24.2.5 (C++0x)
C++03: I think the reduced requirements on the domain of == only applies to input iterators. == is required to be an equivalence relation over its domain for input iterators but for forward iterators and above == is required to be an equivalence relation... full stop.
Yes, i was referring to C++03, I don't know have the 0x draft.
@jweyrich: that would warrant an answer I think :)
@Matthieu M.: Not quite, it's from a not-yet-valid standard. And the current one doesn't have any such requirement, also in the case of the OP it's random access iterators.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.