0

In C++, is there a data structure that allows me to add elements to it within the following for-loop?

(I used a list as an example because that's what I've tried so far.)

list<Elem> elems;
// fill elems with some Elems;

for(list<Elem>::iterator it=elems.begin(); it!=elems.end();) {

    // ...

    if(condition)
        it = elems.erase(it);
    else {
        Elem elem;
        it = elems.push_back(elem);
    }
}

PS: This is a follow-up question to Deleting elements from a vector

Community
  • 1
  • 1
Ben
  • 13,455
  • 15
  • 78
  • 115

4 Answers4

3

This answer on StackOverflow gives a good summary of the behavior of all the standard containers.

The containers that won't invalidate iterators on an insertion or erase are list, set, multiset, map, and multimap. Of course this excludes the iterator being erased.

Community
  • 1
  • 1
Mark Ransom
  • 271,357
  • 39
  • 345
  • 578
1

Yes, the list allows you to do this.. as I said in my comment in the other answer...

Nim
  • 32,149
  • 2
  • 56
  • 98
  • So how do I do it? Because `it = elems.push_back(elem);` can't work (return value is void). – Ben Feb 10 '12 at 16:10
  • if you want to add it to the end, simply call `push_back()`, if you want to insert at the current position, call `insert(it, value);` - this will return you an iterator you can continue with... – Nim Feb 10 '12 at 16:12
  • This means that if I do `it = elems.insert(elem)`, I should be fine? – Ben Feb 10 '12 at 16:15
  • @Ben, the insert function requires the iterator as the current position to insert at.. – Nim Feb 10 '12 at 16:43
1

Writing this per request.

PS: This is a follow-up question to Deleting elements from a vector

This is a troubling pattern in your line of questioning.

Seriously, modifying containers while you're iterating over them is bad juju. It is difficult to get right. It often doesn't save you much in terms of performance. It's rarely (except in limited cases where there's a standard library algorithm exactly suited to your needs) easier to express. It can be very costly in performance if you do it wrong. It can be very costly in terms of program correctness if you do it wrong, which is much more important than anything else.

It gets exponentially worse if threading is involved. Java has a dedicated exception type specifically for certain things that can go wrong when you attempt this. In C++ you aren't so lucky; the analogous problem might not be detectable.

Just write code that produces the final result, then replace the original container. The only thing that can really slow this down is "instances are expensive to copy", in which case you should already be using a layer of indirection to handle the problem (assuming it really matters) anyway.

list<Elem> original;
// fill elems with some Elems;
list<Elem> modified;
for (
    list<Elem>::iterator it = original.begin(), end = original.end();
    it != end; ++it
) {
    if (something()) {
        modified.push_back(*it);
    } else if (something_else()) {
        modified.push_back(Elem()); // for example
    }
    // else, 'erase' the element by just not putting anything into 'modified'
    // Or we could do whatever other combination of things,
    // maybe insert more than one
}
std::swap(original, modified);
Karl Knechtel
  • 51,161
  • 7
  • 77
  • 117
0

There are several containers that support insertion without invalidating iterators. In your code, you have the removal case right, but I believe you're over-thinking the insert case. Try this:

else {
    Elem elem;
    elems.push_back(elems);
    ++it;
}

You're using a list in the given example, so you know the push_back call won't invalidate it. Thus, you can simply increment it to get to the next element. You only have to be careful when removing elements because you can't increment an iterator that points to a place that no longer exists.

Community
  • 1
  • 1
Michael Kristofik
  • 31,476
  • 15
  • 72
  • 121