2016-05-24 6 views
2

Ich habe eine std::list<some_object> events und ich möchte alle Elemente dieser Liste mit Ausnahme der letzten löschen. Also dachte ich, zu tun (wie von this thread vorgeschlagen):C++ Liste löschen bis zum Ende() - 1 Elemente in der Liste

std::list<some_object>::iterator it = events.begin(); // Init iterator on top of list 
while(it!=*std::advance(events.end(),-1)) events.erase(it++); 

Leider über das nicht funktioniert, da es den Fehler produziert:

error: invalid initialization of non-const reference of type ‘std::_List_iterator<node_info>&’ from an rvalue of type ‘std::__cxx11::list<node_info>::iterator {aka std::_List_iterator<node_info>}’ 
      while(it!=*std::advance(event_heap.end(),-1)){ 

Aber nicht list::end soll einen Iterator zurück? Was mache ich falsch?

+1

Warum nicht 'std :: list :: erase' verwenden? – NathanOliver

+1

Was ist los mit 'events.esase (events.begin(), - (events.end()));'? – Beta

+1

Sie sollten 'std :: list :: erase()' verwenden, aber als Alternative könnten Sie vielleicht 'push_back()' das 'back()' Element in eine temp 'std :: list' und dann' std :: swap() 'the temp mit Ihrer Haupt-' std :: list', zB: 'std :: list temp; temp.push_back (events.back()); std :: swap (events, temp); 'Wenn die temp dann den Gültigkeitsbereich verlässt, werden alle Elemente freigegeben, die Sie nicht gespeichert haben. –

Antwort

1

But, isn't list::end supposed to return an iterator?

Ja, aber std::advance nimmt nicht konstante Referenz als ersten Parameter, während event_heap.end() eine temporäre Variable ist und nicht auf nicht konstante Referenz gebunden werden kann.

Und std::advance gibt nichts zurück (d. H. void), so dass Sie nicht operator* darauf verwenden oder es mit it vergleichen können.

Die direkte Befestigung wird wie:

std::list<some_object>::iterator it = events.begin(); 
auto one_before_end = events.end(); 
std::advance(one_before_end, -1); // or --one_before_end; 
while (it != one_before_end) events.erase(it++); 

BTW: std::list::erase hat eine Überlastung Iterator Bereich nehmen, so können Sie einfach:

events.erase(events.begin(), one_before_end); 
+1

Danke @songyuanyao und alle für Ihre aufschlussreichen Antworten! @NathanOliver @Beta: Ich habe vergessen zu erwähnen, dass ich vor dem Löschen einige zusätzliche Operationen an einzelnen Listenelementen vornehmen muss. Daher muss ich die Listenelemente einzeln löschen. Daher ist die 'while'-Schleife wahrscheinlich der einfachste Weg, dies zu tun. Alternativ könnte ich ein Funktor-Prädikat mit 'std :: list :: remove_if' verwenden ... – maurizio

6

Das von std::prev eine typische Anwendung ist. Wenn Sie alle Elemente mit Ausnahme der letzten remove möchten, ist der idiomatische Weg, es zu tun, indem Sie std::prev (die im Grunde verwendet std::advance) auf Ihrem Ende Iterator.

myList.erase(myList.begin(), std::prev(myList.end())); 

Beispiel:

#include <iostream> 
#include <iterator> 
#include <list> 

int main(){ 
    std::list<int> ls = {3, 5, 9, 2}; 

    if(!ls.empty()) 
     ls.erase(ls.begin(), std::prev(ls.end())); 

    for(auto x : ls) 
     std::cout << x << std::endl; 

    return 0; 
} 

Drucke:

2 

Als Remy Lebeau im Kommentar darauf hingewiesen, für nicht C++ 11-Compiler, können Sie einfach std::advance() verwenden, wie erklärt in songyuanyao's answer

+0

Beachten Sie, dass' std :: prev() 'in C++ 11 neu ist. Für frühere Versionen müssen Sie weiterhin 'std :: advance()' verwenden. –

+0

@RemyLebeau, Danke, behoben. :-) – WhiZTiM

+0

Im Allgemeinen sollten Sie '! Ls.empty()' überprüfen, bevor Sie 'prev (ls.end())' oder gleichwertig machen –