Quel est le moyen le plus propre de marcher et de décompresser un vecteur std :: en utilisant des itérateurs?

StackOverflow https://stackoverflow.com/questions/632805

Question

J'ai une situation où je marche à travers un vecteur en faisant des choses:

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

Dans des conditions normales (à supposer que tout se passe bien), je vais jusqu'à my_list.end () et je termine la boucle avec succès.

Cependant, si quelque chose ne va pas pendant que je fais des choses, je veux pouvoir tout annuler - pour en revenir à mes pas jusqu'au début du vecteur, en annulant tout à la fois dans l’ordre inverse.

Mon problème, c’est que lorsque je reçois my_list.begin () - comme indiqué dans la boucle for imbriquée - je n’ai pas encore terminé car j’ai toujours besoin d’appeler undoStuff ( ) sur mon premier élément de la liste. Maintenant, je pourrais faire le dernier appel en dehors de la boucle, mais cela semble un peu impur.

Comme je le vois, je n’ai terminé que lorsque à la liste my_list.rend () . Cependant, je ne peux pas comparer un std :: vector :: iterator à un std :: vector :: reverse_iterator.

Étant donné ce que j'essaie de faire, quel est le meilleur choix de combinaison d'itérateur / type de boucle?

Était-ce utile?

La solution

Bien que l’utilisation d’itérateurs inversés via rbegin () et rend () fonctionne correctement, je trouve malheureusement que la conversion entre des iterarotrs inverses et non inversées a tendance à être assez déroutante. Je ne peux jamais me souvenir sans avoir à passer par un exercice de logique si j'ai besoin d'incrémenter ou de décrémenter avant ou après la conversion. En conséquence, j'évite généralement la conversion.

Voici comment je codifierais probablement votre boucle de traitement des erreurs. Notez que je penserais que vous n’auriez pas à appeler undoStuff () pour l’itérateur qui a échoué - après tout, doStuff () a déclaré qu’il n’avait pas réussi.

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

Autres conseils

Je suis un peu rouillé en ce qui concerne les vecteurs STL, mais serait-il possible de créer un std :: vector :: reverse_iterator à partir de votre itérateur initial? Dans ce cas, il vous suffira de commencer par le dernier élément auquel vous vous trouviez et de pouvoir le comparer à my_list.rend () pour vous assurer que le premier élément est traité.

Il n'y a bien sûr aucune raison de ne pas utiliser les vecteurs opérateur [] () si cela rend votre code plus clair, plus simple et / ou plus efficace.

Cela dépend de ce que fait votre fonction doStuff () et de l'importance des performances dans votre contexte. Si possible, il serait probablement plus clair (c’est-à-dire plus facile pour le lecteur) de travailler sur une copie de votre vecteur, et seulement si tout va bien, permutez les vecteurs.

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

Sans utiliser un reverse_iterator , vous pouvez revenir en arrière de cette façon:

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

Bien que cela crée une copie de iter , le coût ne devrait pas être trop élevé. Vous pouvez refactoriser pour une meilleure vitesse:

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

Vous devez utiliser rbegin () pour obtenir un itérateur réversible.

Personnellement, je préfère toujours

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

Ok, je vais sortir sur une branche ici ..

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

C’est ce que j’appelle l’ingénierie, mais c’est tellement amusant

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

Cela peut être fait avec 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();
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top