std::remove_if - лямбда, ничего не удаляя из коллекции
Хорошо, я ожидаю, что допустил глупую ошибку. У меня есть список DisplayDevice3d, и каждый DisplayDevice3d содержит список DisplayMode3d. Я хочу удалить все элементы из списка DisplayDevice3d, которые не имеют DisplayMode3d. Я пытаюсь использовать лямбда, чтобы сделать это, т.е.
// If the device doesn't have any modes, remove it.
std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(),
[](DisplayDevice3d& device)
{
return device.Modes.size() == 0;
}
);
Несмотря на то, что из 6 объектов DisplayMode3d в MyDisplayDevices только 1 имеет любые DisplayMode3d в своей коллекции режимов, из списка ничего не удаляется.
Какую ошибку я совершил здесь?
Редактировать:
Ах, хорошо, моя ошибка была в том, что я должен был использовать MyDisplayDevices.remove_if вместо std:: remove_if, однако ответы ниже верны для использования std:: remove_if: p.
MyDisplayDevices.remove_if( [](DisplayDevice3d const & device)
{
return device.Modes.size() == 0;
});
4 ответа
Вам нужно вызвать erase для итератора, возвращенного из remove_if, он должен выглядеть примерно так:
auto new_end = std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(),
[](const DisplayDevice3d& device)
{ return device.Modes.size() == 0; });
MyDisplayDevices.erase(new_end, MyDisplayDevices.end());
remove_if
ничего не удаляет из списка, он просто перемещает их в конец. Вы должны использовать его вместе с erase
, Смотрите этот вопрос для более подробной информации.
remove_if
не выполняет изменение размера, но вместо этого просто возвращает итератор к элементу, который следует за последним не удаленным элементом. Этот итератор может быть передан erase()
сделать уборку.
Как уже упоминали другие, есть способы заставить его работать. Однако мой совет будет полностью избегать remove_if
и вместо этого придерживайтесь стандартного удаления на основе итераторов. Идиома ниже работает как для list
а также vector
и не производит неожиданного поведения.
for( vector<TYPE>::iterator iter = vec.begin() ; iter != vec.end() ; )
if( iter->shouldRemove )
iter = vec.erase( iter ) ; // advances iter
else
++iter ; // don't remove
Как отмечается в комментариях ниже, этот метод стоит дороже, чем remove_if
когда удалено более 1 элемента.
remove_if
работает, копируя элементы, расположенные дальше в векторе, и переписывая векторы, которые должны быть удалены из вектора тем, который находится непосредственно перед ним. Например: remove_if вызывается для вектора, чтобы удалить все 0 элементов:
0 1 1 0 1 0
результаты в:
1 1 1 0 1 0
Обратите внимание, что вектор еще не правильный. Это потому remove_if
возвращает итератор к последнему действительному элементу... он не изменяет размер вектора автоматически. Вам все еще нужно позвонить v.erase()
на итераторе, возвращенном из вашего звонка remove_if
,
Пример ниже
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
void print( vector<int> &v )
{
for( int i : v )
printf( "%d ", i );
puts("");
}
int main()
{
vector<int> v = { 0, 1, 1, 0, 1, 0 };
print( v ); // 0 1 1 0 1 0
vector<int>::iterator it = remove_if( v.begin(), v.end(), [](int i){ return i == 0; } );
print( v ); // 1 1 1 0 1 0
v.erase( it, v.end() ); // actually cut out values not wanted in vector
print( v ); // 1 1 1 (correct)
}