Domanda

Ho una situazione in cui sto marciando attraverso un vettore, facendo cose:

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

In condizioni normali - supponendo che tutto vada bene - Marto fino a my_list.end () e termino il ciclo con successo.

Tuttavia, se qualcosa va storto mentre sto facendo delle cose, voglio essere in grado di annullare tutto - fondamentalmente ripercorrere i miei passi fino all'inizio del vettore, annullando tutto uno alla volta in ordine inverso.

Il mio problema è che quando arrivo a my_list.begin () - come mostrato nel ciclo nidificato per - non ho ancora finito perché devo ancora chiamare undoStuff ( ) sul mio primo elemento nell'elenco. Ora, potrei semplicemente effettuare l'ultima chiamata al di fuori del ciclo, ma questo sembra un po 'sporco.

Per come la vedo io, ho finito solo quando arrivo a my_list.rend () . Tuttavia, non posso confrontare uno std :: vector :: iterator con uno std :: vector :: reverse_iterator.

Dato quello che sto cercando di fare, qual è la scelta migliore della combinazione iteratore-tipo / loop?

È stato utile?

Soluzione

Mentre si usano iteratori inversi tramite rbegin () e rend () funziona bene, sfortunatamente trovo che la conversione tra iterarotrs inversi e non inversi tenda a essere piuttosto confusa. Non riesco mai a ricordare senza dover passare attraverso un esercizio di puzzle di logica se devo aumentare o diminuire prima o dopo la conversione. Di conseguenza, evito generalmente la conversione.

Ecco come probabilmente codificherei il tuo ciclo di gestione degli errori. Nota che penso che non dovresti chiamare undoStuff () per l'iteratore fallito - dopotutto, doStuff () ha detto che non è riuscito.

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

Altri suggerimenti

Sono un po 'arrugginito quando si tratta di vettori STL, ma sarebbe possibile creare un std :: vector :: reverse_iterator dal tuo iteratore iniziale? Quindi avresti solo bisogno di iniziare dall'ultimo elemento in cui ti trovavi quando andavi avanti e saresti in grado di confrontarlo con my_list.rend () per assicurarti che il primo elemento sia elaborato.

Ovviamente non c'è motivo di non utilizzare i vettori operator [] () se ciò rende il codice più chiaro, semplice e / o più efficiente.

Dipende dalle funzioni della tua doStuff () e dall'importanza delle prestazioni nel tuo contesto. Se possibile, probabilmente sarebbe più chiaro (cioè - più facile per il lettore) lavorare su una copia del tuo vettore, e solo se tutto va bene, scambia i vettori.

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

Senza usare un reverse_iterator , puoi tornare indietro in questo modo:

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

Sebbene ciò crei una copia di iter , il costo non dovrebbe essere troppo elevato. Puoi refactoring per una migliore velocità:

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

Devi usare rbegin () per ottenere un iteratore reversibile.

Personalmente preferisco ancora

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

Ok, esco di qui un arto ..

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

Questo è ciò che io chiamo sull'ingegneria, ma è così divertente

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

Questo può essere fatto 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();
  }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top