Frage

Ich habe eine Situation, wo ich durch einen Vektor marschieren, Dinge zu tun:

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

Unter normalen Bedingungen - unter der Annahme, alles gut geht -. Ich den ganzen Weg marschieren erfolgreich die Schleife my_list.end() und beenden

Allerdings, wenn etwas schief geht, während ich Sachen mache, möchte ich in der Lage sein, alles rückgängig zu machen -. Im Grunde meine Schritte zurück zum Anfang des Vektors zurückzuverfolgen, alles, was man zu einem Zeitpunkt, in umgekehrter Reihenfolge rückgängig gemacht

Mein Problem ist, dass, wenn ich my_list.begin() bekommen - wie in der verschachtelten for-Schleife gezeigt - ich bin wirklich noch nicht getan, weil ich noch undoStuff() auf mein erstes Element in der Liste aufrufen müssen. Jetzt konnte ich nur den letzten Anruf außerhalb der Schleife, aber dies scheint ein wenig unsauber.

So wie ich es sehe, bin ich nur gemacht, wenn ich my_list.rend() bekommen. Allerdings kann ich nicht ein std :: vector :: iterator zu einem std :: vector :: reverse_iterator vergleichen.

Nach dem, was ich versuche zu tun, was ist die beste Wahl der Iterator-Typ / Schleife Kombination?

War es hilfreich?

Lösung

Bei der Verwendung von Reverse-Iteratoren über rbegin() und rend() gut funktioniert, leider finde ich, dass zwischen Reverse-Umwandlung und nicht umgekehrt iterarotrs ziemlich verwirrend zu sein tendiert. Ich kann nie erinnern, ohne durch eine Logik-Puzzle-Übung gehen, ob ich vor oder nach der Umwandlung erhöhen oder verringern muß. Als Ergebnis vermeide ich im Allgemeinen die Umwandlung.

Hier ist die Art, wie ich wahrscheinlich die Fehlerbehandlung Schleife kodieren würde. Beachten Sie, dass ich denken würde, dass du nicht undoStuff() für den Iterator zu nennen, die fehlgeschlagen -. Immerhin doStuff() sagte es nicht gelungen

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

Andere Tipps

Ich bin ein wenig rostig, wenn es um STL-Vektoren kommt, aber wäre es möglich, eine std::vector::reverse_iterator von Ihrem ersten Iterator zu erstellen? Dann würden Sie erst im letzten Punkt beginnen müssen Sie waren, wenn sie vorwärts gehen, und wäre in der Lage, es zu vergleichen, um my_list.rend(), um sicherzustellen, dass das erste Element verarbeitet wird.

Es gibt natürlich keinen Grund, nicht die Vektoren zu verwenden operator[](), wenn das Ihren Code macht klarer, einfacher und / oder effizienter zu gestalten.

Es hängt davon ab, was Ihr doStuff() funktioniert, und wie wichtig Leistung ist in Ihrem Kontext. Wenn möglich, wäre es wahrscheinlich klarer sein (dh - einfacher für die Leser). Auf einer Kopie des Vektors zu arbeiten, und nur dann, wenn alles in Ordnung ist, tauschen Sie die Vektoren

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

Ohne reverse_iterator zu verwenden, können Sie rückwärts auf diese Weise gehen:

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

Obwohl dies eine Kopie iter schafft, sind die Kosten sollten nicht zu groß. Sie können für eine bessere Geschwindigkeit Refactoring:

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

Sie müssen rbegin () verwenden, eine reversible Iterator zu erhalten.

Persönlich habe ich immer noch lieber

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

Ok, ich werde hier auf ein Bein gehen ..

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

Das ist, was ich über Engineering nennen, aber es ist so viel Spaß

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

Dies kann mit einem reverse_iterator getan werden:

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();
  }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top