2009-12-05 3 views
9

J'ai récemment appris la bonne façon de travailler avec les itérateurs inversés en C++ (en particulier lorsque vous devez en effacer un). (Voir this question et this one.)Utiliser un itérateur régulier pour reculer, ou lutter avec reverse_iterator?

Voici comment vous êtes censé le faire:

typedef std::vector<int> IV; 
for (IV::reverse_iterator rit = iv.rbegin(), rend = iv.rend(); 
    rit != rend; ++rit) 
{ 
    // Use 'rit' if a reverse_iterator is good enough, e.g., 
    *rit += 10; 
    // Use (rit + 1).base() if you need a regular iterator e.g., 
    iv.erase((rit + 1).base()); 
} 

Mais je pense que pensais que c'est beaucoup mieux (Ne le faites pas, pas aux normes compatibles, comme le souligne MooingDuck):

for (IV::iterator it = iv.end(), begin = iv.begin(); 
    it-- != begin;) 
{ 
    // Use 'it' for anything you want 
    *it += 10; 
    iv.erase(it); 
} 

Cons:

  • Vous me dites. Qu'est-ce qui ne va pas?
  • Ce n'est pas conforme aux normes, comme le souligne MooingDuck. Cela annule à peu près tous les avantages possibles ci-dessous.

Plus:

  • Utilise un langage familier pour la marche arrière pour boucles
  • Ne pas à se rappeler (ou expliquer) le +1
  • Moins tapant
  • Works pour std: : liste aussi: it = il.erase(it);
  • Si vous effacez un élément, vous n'avez pas besoin d'ajuster l'itérateur
  • Si vous supprimez, vous ne devez pas recalculer le début iterator
+0

Vous voulez dire en plus du fait que c'est un comportement indéterminé et qu'il échouera/tombera en panne dans des situations courantes? Essayez-le avec une «carte» vide. –

+0

soin d'élaborer dans une réponse? Est-ce que l'UB décrémente un itérateur d'entrée ou décrémente après le début? Est-ce UB pour tous les conteneurs? – Dan

+0

Impossible de décrémenter un itérateur d'entrée ou de sortie (j'ai oublié celui-là, bon oeil), et vous ne pouvez pas non plus diminuer le début d'un conteneur. –

Répondre

7

La raison pour les itérateurs inversés est que les standard algorithms ne savent pas reculer d'une collection. Par exemple:

#include <string> 
#include <algorithm> 
std::wstring foo(L"This is a test, with two letter a's involved."); 
std::find(foo.begin(), foo.end(), L'a'); // Returns an iterator pointing 
             // to the first a character. 
std::find(foo.rbegin(), foo.rend(), L'a').base()-1; //Returns an iterator 
               // pointing to the last A. 
std::find(foo.end(), foo.begin(), L'a'); //WRONG!! (Buffer overrun) 

Utilisez n'importe quel itérateur pour obtenir un code plus clair.

+0

Bon point qu'il peut y avoir des algos génériques qui fonctionnera sur reverse_iterators et il ne peut pas être un " reverse 'version de cet algo pour une utilisation sur des itérateurs réguliers. Pour wstring vous pouvez utiliser find_last_of, mais si c'était un autre type de conteneur qui est pas une option. – Dan

+0

BTW votre deuxième appel std :: find() retourne un itérateur pointant vers '\' »(apostrophe). Cela souligne la 'a': std :: wstring :: iterator iter = (std :: find (foo.rbegin(), foo.rend(), 'a') + 1) .base(); – Dan

+0

Bon point, corrigé :) –

3

Pour ce que ça vaut, Scott Meyers STL efficace recommande que vous venez de coller avec un ol régulier » iterator (point 26).

+2

Il est également indiqué d'éviter les boucles explicites, et 'reverse_iterator' est parfois nécessaire pour accomplir cela. Le point 26 parle uniquement des boucles explicites. –

+0

En outre, cela signifie que le code est bien, quand il est en fait, Undefined Comportement de l'OP (et échouera dans des situations communes) –