2010-12-18 16 views
36

OK, przypuszczam, że popełniłem tu głupi błąd. Mam listę DisplayDevice3d i każdy DisplayDevice3d zawiera listę DisplayMode3d. Chcę usunąć wszystkie elementy z listy DisplayDevice3d, które nie mają żadnych DisplayMode3d. Próbuję użyć Lambda to zrobić, tzn .:std :: remove_if - lambda, nie usuwa niczego z kolekcji

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

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

Nawet z 6 DisplayMode3d w MyDisplayDevices, tylko 1 ma żadnych DisplayMode3d w swojej kolekcji trybach nic nie jest usuwane z listy.

Co tu popełniłem błędny błąd?

Edit:

Ah ok, mój błąd został I powinny być using MyDisplayDevices.remove_if zamiast std :: remove_if jednak poniższe odpowiedzi są prawidłowe dla użycia std :: remove_if: P.

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

Jeśli sam kontener obsługuje remove_if, użyj go. Uważam, że tak jest w przypadku std :: list.W przypadku kontenerów, które nie oferują remove_if, możesz użyć std :: remove_if w połączeniu z funkcją czyszczenia kontenera. – sellibitze

+0

@sellibitze Innymi słowy, szczurze trucizna – bobobobo

+0

możliwy duplikat [Wymazywanie elementów z wektora] (http://stackoverflow.com/questions/347441/erasing-elements-from-a-vector) – bobobobo

Odpowiedz

63

Trzeba zadzwonić wymazać na iterator wrócił z remove_if powinno to wyglądać mniej więcej tak:

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

Działa świetnie. Dzięki. – Robinson

+0

[Jeszcze inna sprawa] (http://stackoverflow.com/a/10360617/111307) – bobobobo

17

remove_if nie usuwa niczego z listy, tylko przesuwa je do końca. Musisz go używać wraz z erase. Więcej informacji na ten temat można znaleźć w question.

+1

Wymazywam z iteratora powraca na koniec listy? – Robinson

+0

@Robinson: tak. – Asha

+4

"po prostu przesuwa je do końca" nie jest dokładnie w porządku. – sellibitze

2

Jak wspominają inni, istnieją sposoby, aby to działało. Jednak moja rada byłaby całkowicie uniknąć remove_if i zamiast tego trzymać się standardowego usuwania iteratora. Poniższy idiom działa zarówno dla list i vector i nie powoduje nieoczekiwanego zachowania.

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

W komentarzach poniżej wzmiankę, metoda ta ma wyższe koszty niż remove_if gdy więcej niż 1 element zostanie usunięty.

remove_if polega na kopiowaniu elementów z dalszej części wektora i nadpisywaniu wektorów, które powinny zostać usunięte z wektora, bezpośrednio przed nim. Na przykład: remove_if nazywa na wektorze, aby usunąć wszystkie 0 elementy:

0 1 1 0 1 0 

skutkuje:

1 1 1 0 1 0 

zauważyć jak wektor nie jest jeszcze prawidłowy. To dlatego, że remove_if zwraca iterator do ostatniego prawidłowego elementu ... nie zmienia automatycznie rozmiaru wektora. Nadal musisz zadzwonić pod numer v.erase() w iteratorze zwróconym z Twojego połączenia pod numer remove_if.

Przykładem jest poniżej

#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

Dlaczego poleciłbyś tę metodę przez * remove_if() *? Z pewnością * remove_if() * nie powoduje "nieoczekiwanego zachowania" (jest to po prostu słabo nazwane = P). A std :: remove_if() dałoby więcej możliwości inteligentnej optymalizacji kompilatora, prawda? Ponieważ iteruje od początku do końca, podczas gdy * gwarantowanie * kompilatorowi, że żadna śmieszna sprawa się nie dzieje, w przeciwieństwie do ręcznego iterowania. (tj. ta sama optymalizacja korzyści * zakres-for() * ma ponad zwykłą * for() *) –

+5

bobobobo: Problem polega na tym, że algorytm jest wolny dla wektora. Każde "wymazanie" spowoduje przesunięcie pozostałych elementów o jeden. Jeśli usuwasz 50 elementów na 1000, to jest to ~ 50 000 ruchów, w których potrzeba było tylko 1000 ruchów rozbitków do ostatnich miejsc. –

5

remove_if nie wykonuje zmiana rozmiaru, ale zamiast po prostu zwraca iterator do elementu, który następuje ostatni element nie został usunięty. Ten iterator można przekazać do erase(), aby przeprowadzić czyszczenie.

enter image description here

Powiązane problemy