2009-12-05 8 views
9

Recientemente aprendí sobre la forma correcta de trabajar con iteradores inversos en C++ (específicamente cuando necesitas borrar uno). (Ver this question y this one.)Use un iterador regular para iterar hacia atrás, o luchar con reverse_iterator?

Ésta es la forma en que se supone que hacerlo:

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

Pero yo creo pensaron que esto es mucho mejor (No haga esto, no estándares compatibles, como señala MooingDuck out):

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

Contras:

  • Dígame. ¿Qué tiene de malo?
  • No cumple con los estándares, como señala MooingDuck. Eso prácticamente anula cualquiera de las posibles ventajas a continuación.

Pros:

  • Utiliza un lenguaje familiar para inversa para bucles
  • no tiene que recordar (o explicar) la +1
  • Menos escribir
  • Obras para std: : list too too: it = il.erase(it);
  • Si borra un elemento, no tiene que ajustar el iterador
  • Si borra, usted no tiene que volver a calcular el iterador comenzar
+0

¿Se refiere además al hecho de que esto es un comportamiento indefinido y fallará/colapsará en situaciones comunes? Pruébalo con un 'mapa' vacío. –

+0

cuidado para elaborar en una respuesta? ¿El UB está decrementando un iterador de entrada o disminuyendo después del comienzo? ¿Es UB para todos los contenedores? – Dan

+0

No se puede disminuir un iterador de entrada o salida (se me olvidó ese, buen ojo), y tampoco se puede disminuir pasado el principio para ningún contenedor. –

Respuesta

7

El motivo de los iteradores inversos es que el standard algorithms no sabe cómo iterar en una colección hacia atrás. Por ejemplo:

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

Utilice el iterador que resulte en un código más claro.

+0

Buen punto de que puede haber algunos algos genéricos que funcionarán en los marcadores inversos y puede que no exista un ' versión inversa de ese algo para uso en iteradores regulares. Para wstring podría usar find_last_of, pero si fuera algún otro tipo de contenedor eso no es una opción. – Dan

+0

BTW su segunda llamada a std :: find() devuelve un iterador que apunta a '\' '(el apóstrofo). Esto apunta a 'a': std :: wstring :: iterator iter = (std :: find (foo.rbegin(), foo.rend(), 'a') + 1) .base(); – Dan

+0

Buen punto, reparado :) –

3

Por lo que vale la pena, Scott Meyers STL eficaz recomienda que sólo se adhieren con un ol regular' iterator (punto 26).

+2

También dice que para evitar los bucles explícitos, y 'reverse_iterator' a veces es necesario para lograr eso. El ítem 26 está hablando solo de bucles explícitos. –

+0

Además, esto implica que el código del OP está bien, cuando en realidad es un Comportamiento no definido (y fallará en situaciones comunes) –