remove from c++ c++11 lambda remove-if erase-remove-idiom

from - vector c++



std:: remove_if-lambda, sin eliminar nada de la colección (4)

Ok, espero haber cometido un error tonto aquí. Tengo una lista de DisplayDevice3d y cada DisplayDevice3d contiene una lista de DisplayMode3d. Quiero eliminar todos los elementos de la lista de DisplayDevice3d que no tienen ningún DisplayMode3d. Estoy tratando de usar un Lambda para hacerlo, es decir:

// If the device doesn''t have any modes, remove it. std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(), [](DisplayDevice3d& device) { return device.Modes.size() == 0; } );

Aunque de los 6 DisplayMode3d en MyDisplayDevices, solo 1 tiene DisplayMode3d en su colección de modos, nada se elimina de la lista.

¿Qué error numpty he hecho aquí?

Editar:

Ah ok, mi error fue que debería usar MyDisplayDevices.remove_if en lugar de std :: remove_if, sin embargo, las respuestas a continuación son correctas para el uso de std :: remove_if: p.

MyDisplayDevices.remove_if( [](DisplayDevice3d const & device) { return device.Modes.size() == 0; });


Como otros han mencionado, hay formas de hacerlo funcionar. Sin embargo, mi consejo sería evitar por completo remove_if y atenerse a una eliminación basada en iterador estándar en su lugar. La expresión a continuación funciona tanto para la list como para el vector y no produce un comportamiento inesperado.

for( vector<TYPE>::iterator iter = vec.begin() ; iter != vec.end() ; ) if( iter->shouldRemove ) iter = vec.erase( iter ) ; // advances iter else ++iter ; // don''t remove

Como se menciona en los comentarios a continuación, este método tiene un costo mayor que remove_if cuando se remove_if más de 1 elemento.

remove_if funciona copiando elementos de más adelante en el vector y sobrescribiendo vectores que deberían ser eliminados del vector por el que está inmediatamente delante de él. Por ejemplo: remove_if llamado en un vector para eliminar los 0 elementos:

0 1 1 0 1 0

resultados en:

1 1 1 0 1 0

Observe cómo el vector aún no es correcto. Esto se debe a que remove_if devuelve un iterador al último elemento válido ... no remove_if automáticamente el tamaño del vector. Aún necesita llamar a v.erase() en el iterador devuelto por su llamada para remove_if .

Un ejemplo está debajo

#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) }


Necesita llamar a borrar en el iterador devuelto desde remove_if, debería verse algo como esto:

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 no elimina nada de la lista, solo los mueve a su final. Debe usarlo junto con erase . Vea esta question para más detalles.


remove_if no realiza el cambio de tamaño, sino que simplemente devuelve el iterador al elemento que sigue al último elemento no eliminado. Este iterador se puede pasar a erase() para hacer la limpieza.