it-swarm.com.de

Löschen beim Durchlaufen einer std :: list

Wenn ich eine iterator in einer for-Schleife verwende und erase für eine aktuelle Iteration des Iterators verwende, sollte die for-Schleife mit dem Rest der list-Elemente fortfahren und darauf zugreifen.

Von dem, was ich gelesen habe, sollte dies der Fall sein und ist ein primäres Unterscheidungsmerkmal von list vs. deque oder vector. Für meine Zwecke könnte eine queue funktionieren, aber ich brauche dieses Verhalten.

Hier ist die Schleife, über die ich nachdenke:

    std::list<Sequence>::iterator iterator;
    iterator=m_concurrents.begin();
    for (;iterator!=m_concurrents.end();++iterator){
        if (iterator->passes()){
            m_concurrents.erase(iterator);
        }
    }
16
johnbakers

Die idiomatische Art, diese Schleife zu schreiben, wäre:

for (auto i = list.begin(); i != list.end();) {
    if (condition)
        i = list.erase(i);
    else
        ++i;
}

Dasselbe können Sie mit einer set, multiset, map oder multimap machen. Bei diesen Containern können Sie ein Element löschen, ohne die Gültigkeit von Iteratoren für andere Elemente zu beeinträchtigen. Andere Container wie vector oder deque sind nicht so freundlich. Für diese Container bleiben nur Elemente vor dem gelöschten Iterator unberührt. Dieser Unterschied besteht einfach darin, dass lists Elemente in individuell zugewiesenen Knoten speichert. Es ist leicht, einen Link herauszunehmen. vectors sind zusammenhängend. Wenn Sie ein Element herausnehmen, werden alle Elemente um eine Position nach hinten verschoben.

Ihre Schleife ist unterbrochen, weil Sie das Element bei i unter einer bestimmten Bedingung löschen. i ist nach diesem Aufruf kein gültiger Iterator mehr. Ihre for-Schleife erhöht dann i, aber i ist nicht gültig. Die Hölle auf Erden folgt. Dies ist die genaue Situation, weshalb erase den Iterator nach dem gelöschten Element an das Element zurückgibt ... Sie können also die list durchlaufen.

Sie können auch list::remove_if verwenden:

list.remove_if([](auto& i) { return i > 10; });

Im Lambda geben Sie true zurück, wenn das Element entfernt werden soll. In diesem Beispiel werden alle Elemente entfernt, die größer als 10 sind.

46
David

Wenn Sie nur für Iterator verwenden möchten, können Sie es beispielsweise wie folgt verwenden:

list<int> lst{4, 1, 2, 3, 5};

for(auto it = lst.begin(); it != lst.end();++it){
    if ((*it % 2) == 1){
        it = lst.erase(it);  erase and go to next(erase will return the next iterator)
        --it;  // as it will be add again in for, so we go back one step
    }
}

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2 

Das Löschen in while Iterator wird jedoch klarer:

list<int> lst{4, 1, 2, 3, 5};

auto it = lst.begin();
while (it != lst.end()){
    if((*it % 2) == 1){
        it = lst.erase(it);// erase and go to next
    } else{
        ++it;  // go to next
    }
}

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

Sie können auch die Member-Funktion remove_if : verwenden.

list<int> lst{4, 1, 2, 3, 5};

lst.remove_if([](int a){return a % 2 == 1;});

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

Oder verwenden Sie std::remove_if conbine mit der Funktion erase:

list<int> lst{4, 1, 2, 3, 5};

lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
    return a % 2 == 1;
}), lst.end());

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

Sie können auch auf diese Frage verweisen: Element aus dem Vektor entfernen, während sich in C++ 11 'for' Schleife? befindet.

0
Jayhello
for (auto i = list.begin(); i != list.end(); ++i) {
    if (condition) {
        list.erase(i);
        --i;
    }
}
0
Frank Hou