fredoverflow
fredoverflow

Reputation: 263118

comparing iterators from different containers

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==.)

Upvotes: 43

Views: 13564

Answers (7)

jweyrich
jweyrich

Reputation: 32240

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.

Upvotes: 39

ds27680
ds27680

Reputation: 1993

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.

Upvotes: 4

CB Bailey
CB Bailey

Reputation: 791729

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.

Upvotes: 2

etarion
etarion

Reputation: 17141

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.

Upvotes: 0

Mahesh
Mahesh

Reputation: 34625

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

Upvotes: 0

Blair Holloway
Blair Holloway

Reputation: 16499

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.

Upvotes: 3

MSalters
MSalters

Reputation: 179799

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

Upvotes: 1

Related Questions