0

This is my code:

std::vector<int> array;
array.push_back(1);
array.push_back(2);
array.push_back(3);
array.push_back(4);
array.push_back(5);

for (int i = 0; i < array.size(); i++) {
    if(array[i]==2 || array[i]==5) {
        array.erase(array.begin() + i);
        printf("### REMOVED ###\n", array[i], i);
    } 

    printf("inside val: %d | index: %d\n", array[i], i);
}

but as you can see, it outputs:

inside val: 1 | index: 0
### REMOVED ###
inside val: 3 | index: 1
inside val: 4 | index: 2
### REMOVED ###
inside val: 5 | index: 3

when my "expectation" is:

inside val: 1 | index: 0
### REMOVED ###
inside val: 2 | index: 1
inside val: 3 | index: 2
inside val: 4 | index: 3
### REMOVED ### 
inside val: 5 | index: 4

it "messes" with index/array's size during manipulation of itself, because will change memory pointer and size of the target.

My habit is to use a foreach statement and iterate a collection (C#/.Net), where even if I remove/add elements during the iteration, the next one is always the next from the beginning list.

How would you do it with C++?

markzzz
  • 47,390
  • 120
  • 299
  • 507

3 Answers3

5

The canonical form of a for loop that may remove elements is the following :

for(auto i = begin(coll); i != end(coll);) {

    // ...

    if(hasToRemove)
        i = coll.erase(i);
    else
        ++i;
}
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 1
    I'm not able to run it: http://cpp.sh/273mz – markzzz Apr 05 '16 at 13:21
  • @markzzz that's because `i` is now an iterator. You must dereference it (`*i`) to access the value it refers to, and you can use `i - begin(coll)` to retrieve the index into the vector. – Quentin Apr 05 '16 at 13:50
  • Can you post a plain example? I'm not able to figure out the results I'd like. – markzzz Apr 05 '16 at 13:51
  • Here what I've done: http://cpp.sh/5no5 – markzzz Apr 05 '16 at 13:52
  • The results are different ### REMOVED ### inside val: 4 | index: 2 inside val: 5 | index: 3 ### REMOVED ### inside val: 5 | index: 4 ### REMOVED ### inside val: 0 | index: 5 inside val: 0 | index: 5 – markzzz Apr 05 '16 at 13:53
  • @markzzz [There you go](http://coliru.stacked-crooked.com/a/4483295800d2ebca). – Quentin Apr 05 '16 at 13:56
  • no its different. `printf` is at the end. Thus, `Erasing element` string must be written before printf (or cout)... – markzzz Apr 05 '16 at 13:59
  • @markzzz If you want to use the value you're iterating over, you need to do so before you erase it. – Quentin Apr 05 '16 at 14:04
  • Who say this? :) No, for some reasons I need to do it after removing the elements. – markzzz Apr 05 '16 at 14:07
  • @markzzz The fact that an erased object is dead, and its storage gets trampled by its neighbour in the case of a vector ;) -- If your real code needs to erase first at all cost, you have to move the object to a local variable beforehand, and use that. – Quentin Apr 05 '16 at 14:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/108288/discussion-between-markzzz-and-quentin). – markzzz Apr 05 '16 at 14:25
  • That's the final working solution: http://cpp.sh/92cat – markzzz Apr 05 '16 at 15:14
2

Use iterators:

int ind = 0;
for (auto i = array.begin(); i != array.end(); ) {
    if(*i==2 || *i==5) {
        i = array.erase(i);
        printf("### REMOVED ###\n");
    } 
    else {
    ++i;
    ++ind;
    }

    printf("inside val: %d | index: %d\n", *i, ind);
}
marcinj
  • 48,511
  • 9
  • 79
  • 100
0

just change your code like this

for (int i = 0; i < array.size(); i++) {
    int temp=i;
    if(array[temp]==2 || array[temp]==5) {
        array.erase(array.begin() + temp);
        printf("### REMOVED ###\n", array[temp], temp);
        i--;
    } 

    printf("inside val: %d | index: %d\n", array[temp], temp);
}
Mehdi
  • 1,731
  • 1
  • 18
  • 33