2017-01-25 10 views
3

У меня возникла проблема, когда я хотел пройти через свой вектор и удалить элементы, которые больше не нужны. Причина того, почему это провалилось, очевидно, но я не видел этого, когда я пробовал свой наивный подход. В принципе, итератор становится недействительным, когда я стираю элемент, и цикл не может продолжаться. То, что я сделал следующий:Каков правильный способ итерации вектора и удаления определенных элементов

#define GOOD 1 
    #define BAD 0 

    struct Element 
    { 
     Element(int isGood) : good(isGood){} 
     bool good; 
    }; 

    int main() 
    { 
     std::vector<Element> arr; 
     arr.push_back(Element(BAD)); 
     arr.push_back(Element(GOOD)); 
     arr.push_back(Element(BAD)); 
     arr.push_back(Element(GOOD)); 

    //__CLEAN ARRAY__// 
     for (auto it = arr.begin(); it != arr.end(); ++it) 
     { 
      if ((*it).good == false) arr.erase(it); 
     } 
    } 

Так что очевидно, что это не будет работать, мне было интересно, что правильный/лучший способ сделать это. Следующим шагом будет перезапуск цикла со свежими итераторами, если не будет найдено ничего хорошего, но это тоже кажется пустой тратой. В идеале цикл продолжится там, где он остановился на свежих итераторах?

Спасибо.

+3

Подробнее о 'std :: remove_if'. –

ответ

3

Вы хотите:

arr.erase(std::remove_if(arr.begin(), arr.end(), [](auto& obj){return obj.good == false;}), arr.end()); 

и его называют удалить стирания идиома:

https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

Но если вы хотите, чтобы исправить цикл, то это возможно, удалить возвращает следующий действительный итератора, чтобы вы должен использовать его:

for (auto it = arr.begin(); it != arr.end();) 
    { 
     if ((*it).good == false) 
     it = arr.erase(it); 
     else 
     it++; 
    } 
+0

Спасибо. Я просто собирался сказать remove и remove_if, не изменяя размер контейнера. Поэтому, если у меня есть 10 элементов и удаляем 2, итератор возвращает новый конец в конце 8 элементов, но итератор container.end() все еще указывает на один конец, 10 + 1. Этот вид делает тогда векторный контейнер бесполезен после этого, не так ли? Кроме того, это влияет на возможность оптимизации для цикла, так как тогда ему придется перепроверить arr.end() каждую итерацию цикла. – Zebrafish

+0

std :: remove_if возвращает новый end() для вектора, а arr.erase использует его для удаления всех удаленных элементов. Итак, после выполнения этого утверждения arr будет иметь 8 элементов. Я не уверен, понимаю ли я ваш второй вопрос, если вы используете цикл, то вам обязательно нужно проверить .end() на каждой итерации. – marcinj

+0

О, я вижу, вы хотите называть стирание на диапазоне после удаления remove_if. То, что я имею в виду, второе, что я упомянул, это то, что итератору не нужно будет проверять каждый раз, если компилятор может знать, что это не изменится. Разве это не одна из оптимизаций, которые делает компилятор? – Zebrafish