Удаление элементов вектора

134
13 октября 2019, 03:00

Подскажите, как корректно пройтись по вектору и удалить некоторые его элементы.
Т.е., корректно ли выполнять такие действия?

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

Или возникнут проблемы с итератором после удаления первого элемента?

Прочитал комментарии, всем спасибо за помощь

Пересмотрел код и понял, что не совсем полно поставил задачу:

for (auto it = arr.begin(); it != arr.end(); it++)
{
    if (check(*it) == false)
    {
        // сделать-что-то полезное с данными, которые потом будут удалены
        arr.erase(it);
    }
}

Если воспользоваться советом @Harry, то тогда код можно же преобразовать к такому:

arr.erase(remove_if(arr.begin(), arr.end(), [](auto x) {
    if (check(x))
    {
        // сделать что-то полезное с данными из x
        return true;
    }
    return false; 
}), arr.end());

?

Answer 1

А почему не так:

arr.erase(remove_if(arr.begin(),arr.end(),
                    [](auto x){ return !check(x); }),
          arr.end());

? Как по мне - понятнее и никаких проблем с итераторами... Вероятно, еще и быстрее, чем убирать по одному элементу...

Если перепишете check так, чтоб работал наоборот - то и лямбда-выражение (или bind) не потребуется.

Если уж позарез нужен цикл - то воспользуйтесь тем, что erase возвращает итератор на элемент, следующий за удаленным:

for(auto i = arr.begin(); i != arr.end();
    i = (check(*i) == false) ? arr.erase(i) : i+1);

Но учтите, что тут сложность - квадратичная, в отличие от remove_if (спасибо @pavel).

Answer 2

erase вернёт следующий итератор, элемент будет удалён, но будет проблема с инкрементом, так что нужно добавить вспомогательный код.

for (auto it = arr.begin(); it != arr.end(); it++)
{
    if (check(*it) == false){
       it = arr.erase(it);
       --it;
    }
}
Answer 3

Итератор после удаления станет не валидным и it++ сделать будет нельзя. К тому же, на каждую итерацию будет перестроение вектора, что в общем случаем считается тяжелой операцией. Можно перенести все правильные значения в новый вектор:

decltype(arr) new_vector;
new_vector.reserve(arr.size());
for (auto i = 0; i < arr.size(); ++i)
{
    new_vector.emplace_back(std::move(arrr[i]));
}
arr.swap(new_vector)
Answer 4

Альтернатива ответу @Komodosh.

Чтобы не декрементировать итератор лишний раз, можно сделать так:

auto it = arr.begin();
while (it != arr.end())
{
    if (check(*it) == false)
        it = arr.erase(it);
    else
        it++;
}

Но лучше послушать Harry и взять erase-remove idiom.

READ ALSO
Отключение исключений и RTTI в VS2017

Отключение исключений и RTTI в VS2017

Смотрел видео Mike Acton и там рассказывалось, что в геймдеве не используют исключения, RTTI и тд

140
Подключение к телеграмму через Telegram Api

Подключение к телеграмму через Telegram Api

создал бота, начал использовать telegram api проект собирается при помощи maven докинул зависимость

123
Как перевести результат вычисления double в понятный формат?

Как перевести результат вычисления double в понятный формат?

входящие данные Числа типа double 20 000 000 общая сумма

130
Что делает обьект типа стринг с массивом

Что делает обьект типа стринг с массивом

Вы этом коде все понимаю кроме этого

147