Std:: remove_if - lambda, не удаляя ничего из коллекции
Хорошо, я ожидаю, что здесь я ошибся. У меня есть список DisplayDevice3d, и каждый DisplayDevice3d содержит список DisplayMode3d. Я хочу удалить все элементы из списка DisplayDevice3d, у которых нет DisplayMode3d. Я пытаюсь использовать Lambda для этого, т.е.:
// 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 в своей коллекции Modes, ничего не удаляется из списка.
Какую ошибку числа я сделал здесь?
Edit:
А, хорошо, моя ошибка заключалась в том, что я должен использовать MyDisplayDevices.remove_if вместо std:: remove_if, однако приведенные ниже ответы верны для использования std:: remove_if: p.
MyDisplayDevices.remove_if( [](DisplayDevice3d const & device)
{
return device.Modes.size() == 0;
});
Ответы
Ответ 1
Вам нужно вызвать 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());
Ответ 2
remove_if
не удаляет что-либо из списка, он просто перемещает их до конца. Вам нужно использовать его вместе с erase
. Подробнее см. question.
Ответ 3
Как уже упоминалось, есть способы заставить его работать. Однако мой совет состоял бы в том, чтобы полностью избежать 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)
}
Ответ 4
remove_if
не выполняет изменение размера, но вместо этого он просто возвращает итератор элементу, который следует за последним не удаленным элементом. Этот итератор можно передать в erase()
для очистки.
![введите описание изображения здесь]()