views:

846

answers:

9

What are the common misuse of using STL containers with iterators?

+20  A: 

Forgetting that iterators are quite often invalidated if you change the container by inserting or erasing container members.

For many great tips on using STL I highly recommend Scott Meyers's book "Effective STL" (sanitised Amazon link)

Rob Wells
I generally agree, but it's possible to interpret your statement as "Iterators are *always* invalidated by insertions or deletions." Vector iterators are never invalidated by deleting a *later* element, and neither insertions nor deletions of other elements invalidate list, set and map iterators.
j_random_hacker
@j_random_hacker: that's true, i'll edit my response
Rob Wells
if possible add some code
yesraaj
+6  A: 

Post-incrementing when pre-incrementing will do.

David Joyner
why the downvote
I can't say this is a big deal as it doesn't compromise code correctness, and surely a decent optimising compiler will generate identical code whenever the original, pre-increment value of i++ is not used? (Mind you I always pre-inc just in case...) FTR: I didn't -1 you (didn't +1 you either...).
j_random_hacker
@j_random_hacker, completely agree that it's not a big deal but it is a common STL iterator idiom. The question didn't specify what specific types issues he was concerned with.
David Joyner
The compiler can't necessarily generate identical code, depending on what the iterator really is. i++ generates a temporary value, which can't always be optimized away. ++i just does the increment.
David Thornley
+7  A: 

The end range check should be using != and not < since the order of the pointers isn't guaranteed.

Example:

for(it = list.begin(); it != list.end(); ++it)
{
     // do stuff
}
Mats Fredriksson
Ouch! Your C++ Standard Library implementation must be using (typedefs of) raw pointer types as iterators -- for exactly this reason, it's much safer to implement all iterators as distinct types that wrap pointers, with suitable operator overloads (e.g. for operator==() but not operator<()).
j_random_hacker
Yeah, was a while since I did c++ to be honest. Never had any issues with it myself since I mostly used vectors. This is one of the things I remember from Exceptional C++ by Herb Sutter. (Or some other similar book)
Mats Fredriksson
You have a typo - you should be comparing "it != list.end()", not "it != list.end". Otherwise you're comparing against the address of a function!
Tom
+6  A: 

Using them without reading the "Effective STL" book by Scott Meyers. :) Really. This makes most of the stupid bugs go away.

Mart Oruaas
As well have having lots of great advice and examples about how best to use the STL.
Richard Corden
+3  A: 

A few others:

  • Converting a reverse iterator to a base iterator without remembering that the iterator will now be one element beyond the one it was pointing to.

  • Trying to use algorithms that require a random-access iterator with iterators of things like sets and maps.

  • Editing the key of a map entry with a non-const iterator (this happens to build on VS.Net, but won't with GCC)

jlarcombe
+5  A: 

Proper continuation after erase().

Assuming:

Container::iterator i = cont.begin(), iEnd = cont.end();

For instance on std::map, this is not a good idea:

for (; i != iEnd; ++i) {
    if (i->second.eraseCondition()) {
        cont.erase(i);
    }
}

This would work:

for (; i != iEnd; ) {
    Container::iterator temp = i;
    ++temp;
    if (i->second.eraseCondition()) {
        cont.erase(i);
    }
    i = temp;
}

And this also:

for (; i != iEnd; ) {
    if (i->second.eraseCondition()) {
        cont.erase(i++);
    }
    else {
        ++i;
    }
}

It's been too many times really that I've had to apply these fixes in some production code :(

Pukku
For this you should be using std::remove_if to prevent just these kinds of bugs.
Billy ONeal
+2  A: 

Using an auto_ptr inside a container, e.g.

list<auto_ptr<int> > foo;

Fortunately, a lot of auto_ptr implementations these days are written to make this impossible.

Mr Fooz
Note: Use boost::shared_ptr<> instead of std::auto_ptr<> if you want to store them in a container.
+1  A: 

This isn't only problem with STL containers - modyfing container when iterating over it almost always leads to problems.

This is very common source of bugs in games - most game loops consists of iterating over each game object doing something. f this something adds or erases elements from game objects container there is almost surelly bug.

Solution - have two containers - objectsToDelete and objectsToAdd - in game code add objects to that containers, and update game objects container only after you iterated over it.

objetsToAdd can be Set to ensure we wont delete anything more than one time.

objectsToDelete can be Queue if you can construct Object without adding it to game objects container, or it can be some other class (ObjectCreateCommand?) if your code assumes that Object instance is always added to game objects container directly after creation (for example in constructor).

+1  A: 
list<int> l1, l2;
// ...

for_each(l1.begin(), l2.end(), do_it());
wilhelmtell