Как удалить элемент из вектора во время цикла?
Я перебираю вектор с петлей, такой как 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), что очень эффективный (без копирования, только изменения внутренних указателей).