views:

1041

answers:

9

Hi! Another newbie question:

Is there any way to check if a iterator (whether it is from a vector, a list, a deque...) is (still) dereferencable, i.e has not been invalidated ? If so, which is the best way, in your opinion? (I was 'trying' and 'catching' :/)

Thanks again!

Edit: An example (which doesn't work)

list<int>l;
for (i=1; i<10; i++) l.push_back(i*10);
itd=l.begin();
itd++;
if (something) l.erase(itd);
/* now, in other place.. check if itd points to somewhere meaningful */
if (itd != l.end())
{
//  blablabla
}
+3  A: 

Usually you test it by checking if it is different from the end(), like

if (it != container.end())
{
   // then dereference
}

Moreover using exception handling for replacing logic is bad in terms of design and performance. Your question is very good and it is definitively worth a replacement in your code. Exception handling like the names says shall only be used for rare unexpected issues.

jdehaan
So, when you destroy the element that the iterator is pointing to in a list, or an element situated before on a vector, the iterator then points to the end? I doesn't, in my case... (I will edit the question to be more clear)
huff
When deleting and inserting, *all* iterators and references might be destroyed. So then you better get new iterators before you continue. This is because a eg. a vector will sometimes have to reallocate all the memory before adding a new item. This will then ofcourse invalidate all pointers, references and iterators (which in most cases are much like pointers)
daramarak
@huff You have to read the API documentation of vector::erase and list::erase to understand the behavior. Further there are some gray areas here where the API was (is it still?) slightly different for Microsoft and GCC implementation of std::map::erase, if I can recall correctly.
Amit Kumar
jdehaan
@jdehaan: I'll check that book. Thx for the recommendation.
huff
+1  A: 
if (iterator != container.end()) {
   iterator is dereferencable !
}

If your iterator doesnt equal container.end(), and is not dereferencable, youre doing something wrong.

Viktor Sehr
+2  A: 

Trying and catching is not safe, you will not, or at least seldom throw if your iterator is "out of bounds".

what alemjerus say, an iterator can always be dereferenced. No matter what uglyness lies beneath. It is quite possible to iterate into other areas of memory and write to other areas that might keep other objects. I have been looking at code, watching variables change for no particular reason. That is a bug that is really hard to detect.

Also it is wise to remember that inserting and removing elements might potentially invalidate all references, pointers and iterators.

My best advice would be to keep you iterators under control, and always keep an "end" iterator at hand to be able to test if you are at the "end of the line" so to speak.

daramarak
With 'can be dereferenced' you probably mean: nobody will prevent you from doing it. However, undefined behavior will occur when dereferencing invalidated iterators.
xtofl
Yes, there is a difference between dereferencing and safely dereferencing...
daramarak
+17  A: 

I assume you mean "is an iterator valid," that it hasn't been invalidated due to changes to the container (e.g., inserting/erasing to/from a vector). In that case, no, you cannot determine if an iterator is (safely) dereferencable.

Jason Govig
Exactly. Thanks.
huff
Although, I think it is time to introduce `Checked STL` into the fray: a checked stl goal is to catch iterators errors > use of invalid iterators or comparison of iterators from different containers among others. A trip by a checked stl should definitely be part of your test suite ;)
Matthieu M.
+5  A: 

As jdehaan said, if the iterator wasn't invalidated and points into a container, you can check by comparing it to container.end().

Note, however, that if the iterator is singular -- because it wasn't initialized or it became invalid after a mutating operation on the container (vector's iterators are invalidated when you increase the vector's capacity, for example) -- the only operation that you are allowed to perform on it is assignment. In other words, you can't check whether an iterator is singular or not.

std::vector<int>::iterator iter = vec.begin();
vec.resize(vec.capacity() + 1);
// iter is now singular, you may only perform assignment on it,
// there is no way in general to determine whether it is singular or not
avakar
I wasn't aware of the term 'singular'. Thanks for teaching!
xtofl
+1  A: 

Is there any way to check if a iterator (whether it is from a vector, a list, a deque...) is (still) dereferencable, i.e has not been invalidated ?

No, there isn't. Instead you need to control access to the container while your iterator exists, for example:

  • Your thread should not modify the container (invalidating the iterator) while it is still using an instantiated iterator for that container

  • If there's a risk that other threads might modify the container while your thread is iterating, then in order to make this scenario thread-safe your thread must acquire some kind of lock on the container (so that it prevents other threads from modifying the container while it's using an iterator)

Work-arounds like catching an exception won't work.

This is a specific instance of the more general problem, "can I test/detect whether a pointer is valid?", the answer to which is typically "no, you can't test for it: instead you have to manage all memory allocations and deletions in order to know whether any given pointer is still valid".

ChrisW
And in a multithreaded scenario, this will suck, won't it?:l.erase(itd); itd = l.end();- And the other thread compares itd to l.end().-Yeah, I know it's not perfect, but the chances of the other thread intervening after the erasing and before the assignation are so remote... eheheh :D
huff
+1  A: 

Non-portable answer: Yes - in Visual Studio

Visual Studio's STL iterators have a "debugging" mode which do exactly this. You wouldn't want to enable this in ship builds (there is overhead) but useful in checked builds.

Read about it on VC10 here (this system can and in fact does change every release, so find the docs specific to your version).

Edit Also, I should add: debug iterators in visual studio are designed to immediately explode when you use them (instead undefined behavior); not to allow "querying" of their state.

Terry Mahaffey
A: 

use erase with increment :

   if (something) l.erase(itd++);

so you can test the validity of the iterator.

lsalamon
A: 

In some of the STL containers, the current iterator becomes invalid when you erase the current value of the iterator. This happens because the erase operation changes the internal memory structure of the container and increment operator on existing iterator points to an undefined locations.

When you do the following, iterator is incementented before it is passed to erase function.

if (something) l.erase(itd++);

lava