2009-12-05 8 views
9

Recentemente ho imparato a conoscere il modo giusto di lavorare con gli iteratori inversi in C++ (in particolare quando è necessario cancellarne uno). (Vedere this question e this one.)Utilizzare un iteratore normale per iterare all'indietro o lottare con reverse_iterator?

Questo è come si suppone di farlo:

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()); 
} 

Ma io penso pensato che questo è molto meglio (non fare questo, non standard compliant, come fa notare MooingDuck):

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

Contro:

  • Dimmi. Cosa c'è di sbagliato in questo?
  • Non è conforme agli standard, come sottolinea MooingDuck. Di seguito si annulla praticamente uno dei possibili vantaggi.

Pro:

  • Utilizza un linguaggio familiare per inversione per-loop
  • non c'è bisogno di ricordare (o spiegare) il +1
  • Meno digitando
  • Works for std: : lista anche: it = il.erase(it);
  • Se si cancella un elemento, non è necessario regolare l'iteratore
  • Se si cancella, non c'è bisogno di ricalcolare l'iteratore
+0

Intendi inoltre il fatto che si tratti di comportamento indefinito e che si verificherà un errore/arresto anomalo in situazioni comuni? Provalo con una 'mappa' vuota. –

+0

cura di elaborare in una risposta? L'UB sta decrementando un iteratore di input o decrementando oltre l'inizio? È UB per tutti i contenitori? – Dan

+0

Impossibile decrementare un input o un iteratore di output (l'ho dimenticato, buon occhio) e non è possibile decrementare l'inizio per nessun contenitore. –

risposta

7

Il motivo per gli iteratori inversi è che lo standard algorithms non sa come eseguire iterazioni su una raccolta al contrario. Ad esempio:

#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) 

Utilizzare qualsiasi iteratore per ottenere un codice più chiaro.

+0

È utile che alcuni alghi generici funzionino su reverse_iterators e potrebbe non esserci un ' Reverse 'versione di quel algo da usare su iteratori regolari. Per wstring potresti usare find_last_of, ma se fosse un altro tipo di contenitore non è un'opzione. – Dan

+0

BTW la seconda chiamata std :: find() restituisce un iteratore che punta a "\" (l'apostrofo). Questo punta a 'a': std :: wstring :: iterator iter = (std :: find (foo.rbegin(), foo.rend(), 'a') + 1) .base(); – Dan

+0

Buon punto, risolto :) –

3

iniziare Per quel che vale, Scott Meyers' Efficace STL raccomanda che hai appena bastone con un ol normale' iterator (punto 26).

+2

Si dice anche di evitare cicli espliciti e talvolta è necessario 'reverse_iterator' per ottenerlo. L'articolo 26 parla solo di loop espliciti. –

+0

Inoltre, questo implica che il codice dell'OP è soddisfacente, quando è in effetti, comportamento non definito (e fallirà in situazioni comuni) –

Problemi correlati