Удаление элементов из вектора с помощью remove_if
Я пытаюсь удалить векторные элементы using remove_if
. Но безуспешно. Что я делаю неправильно?
Вот мой код:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
void printme(std::vector<int>& a){
for(const auto& item: a)
std::cout << item << std::endl;
}
int main()
{
std::vector<int> a {1, 2, 3, 4, 5, 6};
printme(a);
a.erase( (std::remove_if(a.begin(), a.end(), [](const int& x){
return x == 2;
}), a.end()));
printme(a);
}
Мой результат:
1 2 3 4 5 6
Ожидаемый результат:
1 2 3 4 5 6 1 3 4 5 6
Ответы
Ответ 1
Вы используете перегрузку функции std::vector::erase()
которая принимает один итератор в качестве параметра. В качестве аргумента для erase()
вы предоставляете итератор a.end()
, поскольку следующее выражение:
(std::remove_if(a.begin(), a.end(), [](const int& x){ return x == 2; }), a.end()))
оценивается как a.end()
(т.е. из-за оператора запятой).
Итератор перешел к перегрузке erase()
который принимает один итератор, должен быть разыменован. Однако итератор a.end()
не является разыменованным, поэтому вызов erase()
приводит к неопределенному поведению.
Чтобы использовать перегрузку, которая принимает два итератора, удалите скобки вокруг вызова в std::remove_if
:
a.erase(std::remove_if(a.begin(), a.end(), [](const int& x){
return x == 2;
}), a.end());
Ответ 2
Вы добавляете лишние круглые скобки, меняете их на
a.erase( std::remove_if(a.begin(), a.end(), [](const int& x){
return x == 2;
}), a.end());
Обратите внимание, что оператор запятой просто возвращает последний операнд, это означает, что вы передаете a.end()
для erase
, что приводит к UB.
Ответ 3
В других ответах указывается, в чем проблема. Я хочу сказать, что будет легче заметить эти проблемы, упростив ваш код.
Я предлагаю использовать:
int main()
{
std::vector<int> a {1, 2, 3, 4, 5, 6};
printme(a);
auto it = std::remove_if(a.begin(), a.end(), [](const int& x){ return x == 2; });
a.erase(it, a.end());
printme(a);
}
Ответ 4
В вызове функции было слишком много скобок.
a.erase(std::remove_if(a.begin(), a.end(), [](const int& x) {return x == 2;}), a.end());
Просто удалите одну скобку перед std::remove_if
и в конце вызова.
Ответ 5
Ваша проблема заключается в том, что вы выполняете стирание-удаление идиомы inline. Он очень подвержен ошибкам.
template<class C, class F>
void erase_remove_if( C&& c, F&& f ) {
using std::begin; using std::end;
auto it = std::remove_if( begin(c), end(c), std::forward<F>(f) );
c.erase( it, end(c) );
}
эта небольшая вспомогательная функция делает ошибку, подверженную стиранию, удаляться отдельно от других шумов.
Затем:
a.erase( (std::remove_if(a.begin(), a.end(), [](const int& x){
return x == 2;
}), a.end()));
становится
erase_remove_if(
a,
[](const int& x){
return x == 2;
}
);
и вдруг ваш код работает.
Теперь ближайшая причина: у вас была опечатка:
a.erase(
(
std::remove_if(
a.begin(),
a.end(),
[](const int& x){
return x == 2;
}
),
a.end()
)
);
здесь я расширил структуру строки. Вы можете видеть из вышесказанного, что вы только передали один аргумент для erase
; а именно: a.end()
, потому что вы его передали ( some remove expression, a.end() )
в скобках. Это вызвало оператор запятой: поэтому он запускал выражение remove (перемещение элемента 2
до конца), затем отбрасывал возвращенный итератор и оценивался как a.end()
.
Затем мы передали a.end()
для erase
, который не является допустимым итератором для передачи для erase
. Таким образом, ваша программа плохо сформирована и результаты UB.
Это только ближайшая причина. Есть много ошибок, которые вы можете легко сделать, когда вручную удаляете удаление; код является хрупким и полным повторения.
DRY - это принцип, что вам нужна одна точка настройки, и вы не хотите повторять то, что не нужно повторять. erase_remove_if
- моя попытка применить DRY, чтобы избежать такой ошибки.