Удаление элементов из вектора с помощью remove if
Я пытаюсь удалить векторные элементы с помощью 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
5 ответов
вы используете перегрузку 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());
вы добавляете лишние скобки, измените его на
a.erase( std::remove_if(a.begin(), a.end(), [](const int& x){
return x == 2;
}), a.end());
отметим, что оператор запятая просто возвращает последний операнд, это означает, что вы передаете a.end()
to erase
, что приводит к UB.
другие ответы указали, в чем проблема. Я хочу сказать, что будет легче заметить такие проблемы, упростив ваш код.
Я предлагаю использовать:
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);
}
ты просто слишком много скобок в вызове функции.
a.erase(std::remove_if(a.begin(), a.end(), [](const int& x) {return x == 2;}), a.end());
просто удалите одну скобку перед std::remove_if
и в конце разговора.
ваша проблема в том, что вы делаете идиому стирания-удаления 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()
to erase
, который не является допустимым итератором для передачи в erase
. Таким образом, ваша программа плохо сформирована, и результаты UB.
это только экспресс причина. Есть много ошибок, которые вы можете легко сделать при ручном выполнении стирания-удаления; код хрупкий и полный повторений.
сухой принцип, что вы хотите одну точку настройки, и вы не хотите повторять вещи, которые не нуждаются в повторении. erase_remove_if
моя попытка применить сухой, чтобы избежать именно такой ошибки.