web-dev-qa-db-fra.com

Comment procédez-vous en arrière dans une liste STL?

J'écris du code multiplateforme entre Windows et Mac.

Si list :: end () "renvoie un itérateur qui adresse l'emplacement qui succède au dernier élément d'une liste" et peut être vérifié lors de la traversée d'une liste vers l'avant, quelle est la meilleure façon de traverser vers l'arrière?

Ce code fonctionne sur le Mac mais pas sur Windows (ne peut pas décrémenter au-delà du premier élément):

list<DVFGfxObj*>::iterator iter = m_Objs.end();
for (iter--; iter!=m_Objs.end(); iter--)// By accident discovered that the iterator is circular ?
{
}

cela fonctionne sous Windows:

list<DVFGfxObj*>::iterator iter = m_Objs.end();
    do{
        iter--;
    } while (*iter != *m_Objs.begin());

Existe-t-il un autre moyen de parcourir vers l'arrière qui pourrait être implémenté dans une boucle for?

35
AlanKley

Utilisez reverse_iterator au lieu d'itérateur. Utilisez rbegin () & rend () au lieu de begin () & end ().

Une autre possibilité, si vous aimez utiliser la macro BOOST_FOREACH est d'utiliser la macro BOOST_REVERSE_FOREACH introduite dans Boost 1.36.0.

60
Ferruccio

La manière la meilleure/la plus simple pour inverser l'itération d'une liste est (comme déjà indiqué) d'utiliser les itérateurs inverses rbegin/rend.

Cependant, je voulais mentionner que les itérateurs inverses sont implémentés en stockant la position "actuelle" de l'itérateur un par un (au moins sur l'implémentation GNU de la bibliothèque standard).

Ceci est fait pour simplifier l'implémentation, afin que la plage inversée ait la même sémantique qu'une plage avant [début, fin) et [rbegin, rend)

Cela signifie que le déréférencement d'un itérateur implique la création d'un nouveau temporaire, puis sa décrémentation, à chaque fois:

  reference
  operator*() const
  {
_Iterator __tmp = current;
return *--__tmp;
  }

Ainsi, déréférencer un inverseur_itérateur est plus lent qu'un itérateur normal.

Cependant, vous pouvez utiliser à la place les itérateurs bidirectionnels réguliers pour simuler vous-même l'itération inverse, en évitant cette surcharge:

for ( iterator current = end() ; current != begin() ; /* Do nothing */ )
{
    --current; // Unfortunately, you now need this here
    /* Do work */
    cout << *current << endl;
}

Les tests ont montré que cette solution était ~ 5 fois plus rapide pour chaque déréférence utilisée dans le corps de la boucle.

Remarque: Les tests n'ont pas été effectués avec le code ci-dessus, car std :: cout aurait été le goulot d'étranglement.

Remarquez également: la différence de "temps d'horloge murale" était de ~ 5 secondes avec une taille std :: list de 10 millions d'éléments. Donc, de façon réaliste, à moins que la taille de vos données ne soit aussi grande, respectez simplement rbegin () rend ()!

16
mmocny

Vous voulez probablement les itérateurs inverses. De mémoire:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for( ; iter != m_Objs.rend(); ++iter)
{
}
14
Anthony Cramp

Comme déjà mentionné par Ferruccio, utilisez reverse_iterator:

for (std::list<int>::reverse_iterator i = s.rbegin(); i != s.rend(); ++i)
5
ejgottl

Cela devrait fonctionner:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for (; iter!= m_Objs.rend(); iter++)
{
}
5
steffenj