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