Удаление элементов из вектора с помощью 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 моя попытка применить сухой, чтобы избежать именно такой ошибки.