Как удалить элемент из вектора во время цикла?

Я перебираю вектор с петлей, такой как for(int i = 0; i < vec.size(); i++). В этом цикле я проверяю условие на элемент в этом векторном индексе, и если какое-то условие истинно, я хочу удалить этот элемент.

Как удалить векторный элемент во время цикла без него?

Ответы

Ответ 1

Идиоматический способ удаления всех элементов из контейнера STL, который удовлетворяет заданному предикату, заключается в использовании удаления-удаления идиомы. Идея состоит в том, чтобы переместить предикат (функцию, которая возвращает true или false для некоторого элемента) в заданную функцию, например pred, а затем:

static bool pred( const std::string &s ) {
  // ...
}

std::vector<std::string> v;
v.erase( std::remove_if( v.begin(), v.end(), pred ), v.end() );

Если вы настаиваете на использовании индексов, вам не следует увеличивать индекс для каждого элемента, но только для тех, которые не удаляются:

std::vector<std::string>::size_type i = 0;
while ( i < v.size() ) {
    if ( shouldBeRemoved( v[i] ) ) {
        v.erase( v.begin() + i );
    } else {
        ++i;
    }
}

Однако это не только больше кода и менее идиоматический (читайте: программисты на C++ действительно должны смотреть на код, тогда как идиома "стереть и удалить" сразу же дает некоторое представление о том, что происходит), но также гораздо менее эффективна, потому что векторы сохраняя их элементы в одном непрерывном блоке памяти, поэтому стирание на позициях, отличных от конца вектора, также перемещает все элементы после стирания сегмента до их новых позиций.

Ответ 2

Если вы не можете использовать remove/erase (например, потому что вы не хотите использовать lambdas или писать предикат), используйте стандартную идиому для удаления элементов контейнера последовательности:

for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */)
{
    if (delete_condition)
    {
        it = v.erase(it);
    }
    else
    {
        ++it;
    }
}

Если возможно, предпочитаем удалять/стирать:

#include <algorithm>

v.erase(std::remove_if(v.begin(), v.end(),
                       [](T const & x) -> bool { /* decide */ }),
        v.end());

Ответ 3

Используйте Erase-Remove Idiom, используя remove_if с предикатом, чтобы указать ваше условие.

Ответ 4

if(vector_name.empty() == false) {
    for(int i = vector_name.size() - 1; i >= 0; i--)
    {
        if(condition) 
            vector_name.erase(vector_name.at(i));
    }
}

Это работает для меня. И не нужно думать, что индексы уже стерты.

Ответ 5

Итерации над вектором назад. Таким образом, вы не обладаете способностью добираться до тех элементов, которые вы еще не посетили.

Ответ 6

Я понимаю, что вы спрашиваете конкретно об удалении из вектора, но просто хотели указать, что дорого стоит удалить элементы из std::vector, поскольку все элементы после удаляемого элемента должны быть скопированы в новое место. Если вы собираетесь удалить элементы из контейнера, вы должны использовать std:: list. Метод std:: list:: erase (item) даже возвращает итератор, указывающий на значение после только что стертого, поэтому его легко использовать в цикле for или while. Хорошая вещь также с std:: list заключается в том, что итераторы, указывающие на не стираемые элементы, остаются в силе в течение всего списка. См. Например, docs на cplusplus.com.

Тем не менее, если у вас нет выбора, трюк, который может работать, это просто создать новый пустой вектор и добавить элементы к нему из первого вектора, а затем использовать std:: swap (oldVec, newVec), что очень эффективный (без копирования, только изменения внутренних указателей).