Pregunta

Tengo una situación en la que marcho a través de un vector, haciendo cosas:

std::vector::iterator iter = my_list.begin();

for ( ; iter != my_list.end(); ++iter )
{
  if ( iter->doStuff() )   // returns true if successful, false o/w
  {
    // Keep going...
  }
  else
  {
    for ( ; iter != m_list.begin(); --iter )  // ...This won't work...
    {
      iter->undoStuff();
    }
  }
}

En condiciones normales, suponiendo que todo vaya bien, marcho hasta my_list.end () y finalizo el ciclo con éxito.

Sin embargo, si algo sale mal mientras estoy haciendo cosas, quiero poder deshacer todo, básicamente, volver sobre mis pasos al comienzo del vector, deshacer todo de a uno en orden inverso.

Mi problema es que cuando llego a my_list.begin () , como se muestra en el bucle anidado, todavía no he terminado porque todavía necesito llamar a undoStuff ( ) en mi primer elemento en la lista. Ahora, podría hacer la última llamada fuera del ciclo, pero esto parece un poco sucio.

A mi modo de ver, solo termino cuando llego a my_list.rend () . Sin embargo, no puedo comparar un std :: vector :: iterator con un std :: vector :: reverse_iterator.

Dado lo que estoy tratando de hacer, ¿cuál es la mejor opción de combinación iterador-tipo / bucle?

¿Fue útil?

Solución

Al usar iteradores inversos a través de rbegin () y rend () funciona bien, desafortunadamente encuentro que la conversión entre iterarotrs inversos y no inversos tiende a ser bastante confusa. Nunca puedo recordar sin tener que pasar por un ejercicio de rompecabezas lógico si necesito aumentar o disminuir antes o después de la conversión. Como resultado, generalmente evito la conversión.

Esta es la forma en que probablemente codifique su ciclo de manejo de errores. Tenga en cuenta que creo que no tendría que llamar a undoStuff () para el iterador que falló; después de todo, doStuff () dijo que no tuvo éxito.

// handle the situation where `doStuff() failed...

// presumably you don't need to `undoStuff()` for the iterator that failed
// if you do, I'd just add it right here before the loop:
//
//     iter->undoStuff();

while (iter != m_list.begin()) {
    --iter;
    iter->undoStuff();
}

Otros consejos

Estoy un poco oxidado cuando se trata de vectores STL, pero ¿sería posible crear un std :: vector :: reverse_iterator desde su iterador inicial? Luego, solo necesitaría comenzar en el último elemento en el que se encontraba al avanzar, y podría compararlo con my_list.rend () para asegurarse de que se procese el primer elemento.

Por supuesto, no hay razón para no usar los vectores operator [] () si eso hace que su código sea más claro, simple y / o más eficiente.

Depende de lo que haga su función doStuff () y de cuán importante sea el rendimiento en su contexto. Si es posible, probablemente sería más claro (es decir, más fácil para el lector) trabajar en una copia de su vector, y solo si todo está bien, cambie los vectores.

std::vector<Foo> workingCopy;
workingCopy.assign(myVector.begin(), myVector.end());

bool success = true;
auto iter = workingCopy.begin();
for( ; iter != workingCopy.end() && success == true; ++iter )
    success = iter->doStuff();

if( success )
    myVector.swap(workingCopy);

Sin usar un reverse_iterator , puede caminar hacia atrás de esta manera:

while(iter-- != m_list.begin())
{
    iter->undoStuff();
}

Aunque esto crea una copia de iter , el costo no debería ser demasiado alto. Puede refactorizar para una mejor velocidad:

while(iter != m_list.begin())
{
    --iter;
    iter->undoStuff();
}

Debe usar rbegin () para obtener un iterador reversible.

Personalmente todavía prefiero

for (int i=0;i<vecter.size();i++) { }

Ok, me arriesgaré aquí ...

std::vector iterator iter = my_list.begin();
bool error = false;

while(iter != my_list.end())
{
  error = !iter->doStuff();
  if(error)
    break
  else
    iter++;
}

if(error)
do
{
  iter->undoStuff();
  iter--;
} 
while(iter != my_list.begin())

Esto es lo que llamo sobre ingeniería, pero es muy divertido

// This also can be done with adaptators I think
// Run DoStuff until it failed or the container is empty
template <typename Iterator>
Iterator DoMuchStuff(Iterator begin, Iterator end) {
  Iterator it = begin;
  for(; it != end; ++it) {
    if(!*it->DoStuff()) {
      return it;
    }
  }
  return it;
}

// This can be replaced by adaptators
template <typename Iterator>
void UndoMuchStuff(Iterator begin, Iterator end) {
  for(Iterator it = begin; it != end; ++it) {
    it->UndoStuff();
  }
}

// Now it is so much easier to read what we really want to do
typedef std::vector<MyObject*> MyList;
typedef MyList::iterator Iterator;
typedef MyList::reverse_iterator ReverseIterator;
Iterator it = DoMuchStuff(my_list.begin(), my_list.end());
if(it != my_list.end()) {
  // we need to unprocess [begin,it], ie including it
  UndoMuchStuff(ReverseIterator(1+it), ReverseIterator(my_list.begin()));
}

Esto se puede hacer con un reverse_iterator :

bool shouldUndo(false);
std::vector::iterator iter(my_list.begin()), end(my_list.end());
for ( ; iter != end && !shouldUndo; ++iter )
{
  shouldUndo = iter->doStuff();   // returns true if successful, false o/w
}
if (shouldUndo) {
  reverse_iterator<std::vector::iterator> riter(iter), rend(my_list.rend());
  //Does not call `undoStuff` on the object that failed to `doStuff`
  for ( ; riter != rend; ++riter )
  {
    iter->undoStuff();
  }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top