Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is comparing against "end()" iterator legal?

According to C++ standard (3.7.3.2/4) using (not only dereferencing, but also copying, casting, whatever else) an invalid pointer is undefined behavior (in case of doubt also see this question). Now the typical code to traverse an STL containter looks like this:

std::vector<int> toTraverse;
//populate the vector
for( std::vector<int>::iterator it = toTraverse.begin(); it != toTraverse.end(); ++it ) {
    //process( *it );
}

std::vector::end() is an iterator onto the hypothetic element beyond the last element of the containter. There's no element there, therefore using a pointer through that iterator is undefined behavior.

Now how does the != end() work then? I mean in order to do the comparison an iterator needs to be constructed wrapping an invalid address and then that invalid address will have to be used in a comparison which again is undefined behavior. Is such comparison legal and why?

like image 464
sharptooth Avatar asked Apr 28 '10 09:04

sharptooth


People also ask

Can you dereference an end iterator?

In C++, you cannot dereference an iterator straight away because the end() function returns an iterator and object as a pointer, which isn't a valid member of the data structure. When you dereference at the end, it throws an error because the purpose of the end pointer is only to see when you have reached it.

What happens when you dereference an iterator?

3. Dereferencing: An input iterator can be dereferenced, using the operator * and -> as an rvalue to obtain the value stored at the position being pointed to by the iterator. 4. Incrementable: An input iterator can be incremented, so that it refers to the next element in the sequence, using operator ++().

How do you compare two iterators?

we can use == and != to compare to valid iterators into any of the library containers. The section also tells us that iterators for string and vector support relational operators (aka iterator arithmetic) which include >, >=, <, <=.

What is an end iterator?

Not just arrays; the end iterator points past the end of the target sequence, regardless of where the the iterators come from or where the values are held.


3 Answers

The only requirement for end() is that ++(--end()) == end(). The end() could simply be a special state the iterator is in. There is no reason the end() iterator has to correspond to a pointer of any kind.

Besides, even if it were a pointer, comparing two pointers doesn't require any sort of dereference anyway. Consider the following:

char[5] a = {'a', 'b', 'c', 'd', 'e'};
char* end = a+5;
for (char* it = a; it != a+5; ++it);

That code will work just fine, and it mirrors your vector code.

like image 50
Nick Lewis Avatar answered Oct 15 '22 03:10

Nick Lewis


You're right that an invalid pointer can't be used, but you're wrong that a pointer to an element one past the last element in an array is an invalid pointer - it's valid.

The C standard, section 6.5.6.8 says that it's well defined and valid:

...if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object...

but cannot be dereferenced:

...if the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated...

like image 34
JoeG Avatar answered Oct 15 '22 01:10

JoeG


One past the end is not an invalid value (neither with regular arrays or iterators). You can't dereference it but it can be used for comparisons.

std::vector<X>::iterator it;

This is a singular iterator. You can only assign a valid iterator to it.

std::vector<X>::iterator it = vec.end();

This is a perfectly valid iterator. You can't dereference it but you can use it for comparisons and decrement it (assuming the container has a sufficient size).

like image 20
UncleBens Avatar answered Oct 15 '22 01:10

UncleBens