2010-12-18 14 views
36

Ok, supongo que cometí 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 intentando utilizar un Lambda de hacerlo, es decir .:std :: remove_if - lambda, sin eliminar nada de la colección

// If the device doesn't have any modes, remove it. 

    std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(), 
    [](DisplayDevice3d& device) 
    { 
    return device.Modes.size() == 0; 
    } 
); 

A pesar de que de cada 6 de DisplayMode3d en MyDisplayDevices, solamente 1 tiene alguna de DisplayMode3d en su colección modos, nada se va a quitar de la lista.

¿Qué error numpty he hecho aquí?

Editar:

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

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

Si el propio recipiente soporta remove_if entonces por todos los medios la utilizan. Creo que este es el caso con std :: list.Para contenedores que no ofrecen remove_if, puede usar std :: remove_if en combinación con la función de miembro de borrado del contenedor. – sellibitze

+0

@sellibitze En otras palabras, veneno para ratas – bobobobo

+0

posible duplicado de [Borrando elementos de un vector] (http://stackoverflow.com/questions/347441/erasing-elements-from-a-vector) – bobobobo

Respuesta

63

es necesario llamar a borrar en el iterador regresó de remove_if, debe 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()); 
+0

Funciona muy bien. Gracias. – Robinson

+0

[Otra opinión sobre esto] (http://stackoverflow.com/a/10360617/111307) – bobobobo

17

remove_if no quita nada de la lista sólo los mueve a fin. Debe usarlo junto con erase. Consulte esto question para más detalles.

+1

Así que borro del iterador vuelve al final de la lista? – Robinson

+0

@Robinson: sí. – Asha

+4

"simplemente los mueve hasta el final" no es exactamente correcto. – sellibitze

2

Como otros han mencionado, hay maneras de hacer que funcione. Sin embargo, mi consejo sería evitar por completo remove_if y atenerse a una eliminación estándar basada en iterador en su lugar. La expresión a continuación funciona tanto para list como para 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 los comentarios a continuación mención, este método tiene un costo mayor que remove_if cuando se retira más de 1 elemento.

remove_if funciona copiando elementos de más adelante en el vector, y sobrescribiendo los vectores que deben eliminarse del vector por el inmediatamente anterior. Por ejemplo: remove_if llamada en un vector para eliminar todos los elementos de 0:

0 1 1 0 1 0 

resultados en:

1 1 1 0 1 0 

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

Un ejemplo está por 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) 
} 
+3

¿Por qué recomendarías este método sobre * remove_if() *? Seguramente * remove_if() * tampoco produce "comportamiento inesperado" (simplemente se llama mal = P). Y std :: remove_if() proporcionaría más oportunidades para que el compilador optimice inteligentemente, ¿no es así? Porque itera de principio a fin, mientras que * garantiza * al compilador que no está ocurriendo ningún negocio divertido, a diferencia de la iteración manual. (es decir, los mismos beneficios de optimización * range-for() * sobre regulares * para() *) –

+5

bobobobo: El problema es que su algoritmo es lento para vector. Cada 'borrado' desplazará los elementos restantes hacia abajo en uno. Si borras 50 elementos de 1000, eso es ~ 50,000 movimientos, donde solo necesitas ~ 1000 movimientos de los supervivientes a sus ranuras finales. –

5

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

enter image description here

Cuestiones relacionadas