2009-12-05 2 views
9

Ich habe vor kurzem gelernt, wie man mit umgekehrten Iteratoren in C++ richtig arbeitet (speziell wenn man einen löschen muss). (Siehe this question und this one.)Verwenden Sie einen regulären Iterator, um rückwärts zu iterieren, oder kämpfen Sie mit reverse_iterator?

Dies ist, wie man eigentlich, es zu tun:

typedef std::vector<int> IV; 
for (IV::reverse_iterator rit = iv.rbegin(), rend = iv.rend(); 
    rit != rend; ++rit) 
{ 
    // Use 'rit' if a reverse_iterator is good enough, e.g., 
    *rit += 10; 
    // Use (rit + 1).base() if you need a regular iterator e.g., 
    iv.erase((rit + 1).base()); 
} 

Aber ich denke, dachte, das viel besser ist (diese nicht tun, nicht Normen konform, wie MooingDuck weist darauf hin):

for (IV::iterator it = iv.end(), begin = iv.begin(); 
    it-- != begin;) 
{ 
    // Use 'it' for anything you want 
    *it += 10; 
    iv.erase(it); 
} 

Nachteile:

  • Sie sagen mir. Was ist los damit?
  • Es ist nicht standardkonform, wie MooingDuck herausfindet. Das übersieht praktisch alle möglichen Vorteile unten.

Vorteile:

  • Verwendet eine vertraute Idiom für Reverse-for-Schleifen
  • Sie müssen sich nicht erinnern (oder zu erklären) die +1
  • Weniger
  • Works für std eingeben: : list too: it = il.erase(it);
  • Wenn Sie ein Element löschen, müssen Sie den Iterator
  • nicht anpassen
  • Wenn Sie löschen, müssen Sie nicht den Iterator beginnen neu berechnen
+0

Sie meinen neben der Tatsache, dass dies Undefined Behavior ist und in allgemeinen Situationen fehlschlagen/abstürzen wird? Probieren Sie es mit einer leeren 'map' aus. –

+0

Pflege in einer Antwort zu erarbeiten? Verringert der UB einen Eingabe-Iterator oder dekrementiert er den Anfang? Ist es UB für alle Behälter? – Dan

+0

Ich kann einen Eingabe- oder Ausgabe-Iterator nicht dekrementieren (ich habe das vergessen, gutes Auge), und Sie können auch nicht für irgendeinen Container über den Anfang hinaus dekrementieren. –

Antwort

7

Der Grund für umgekehrte Iteratoren ist, dass die standard algorithms nicht wissen, wie man über eine Sammlung rückwärts iteriert. Zum Beispiel:

#include <string> 
#include <algorithm> 
std::wstring foo(L"This is a test, with two letter a's involved."); 
std::find(foo.begin(), foo.end(), L'a'); // Returns an iterator pointing 
             // to the first a character. 
std::find(foo.rbegin(), foo.rend(), L'a').base()-1; //Returns an iterator 
               // pointing to the last A. 
std::find(foo.end(), foo.begin(), L'a'); //WRONG!! (Buffer overrun) 

Verwenden Sie den Iterator, der zu einem klareren Code führt.

+0

Guter Punkt, dass es einige generische Algos, die auf reverse_iterators arbeiten können und möglicherweise nicht ein ' Reverse-Version dieses Algo für die Verwendung auf regulären Iteratoren. Für wstring könnten Sie find_last_of verwenden, aber wenn es sich um eine andere Art von Container handelt, ist das keine Option. – Dan

+0

BTW Ihr zweiter Aufruf std :: find() gibt einen Iterator zurück, der auf '\' '(das Apostroph) zeigt. Dies zeigt auf das 'a': std :: wstring :: iterator iter = (std :: finden (foo.rbegin(), foo.rend(), 'a') + 1) .base(); – Dan

+0

Guter Punkt, behoben :) –

3

Für das, was es wert ist, Scott Meyers' Effective STL empfiehlt, dass Sie nur mit einem regelmäßigen ol kleben‘iterator (Punkt 26).

+2

Es wird auch gesagt, um explizite Schleifen zu vermeiden, und manchmal ist "reverse_iterator" erforderlich, um dies zu erreichen. Punkt 26 spricht nur von expliziten Schleifen. –

+0

Dies bedeutet auch, dass der OP-Code in Ordnung ist, wenn es in der Tat ist, Undefined Behavior (und wird in allgemeinen Situationen fehlschlagen) –