Que se passe-t-il si vous incrémentez un iterator égal à l’iterator de fin d’un conteneur STL?

Que se passe-t-il si j’incrémente un iterator de 2 lorsqu’il pointe sur le dernier élément d’un vecteur? Dans cette question demandant comment ajuster l’iterator à un conteneur STL par 2 éléments, deux approches différentes sont proposées:

  • soit utiliser une forme d’opérateur arithmétique – + = 2 ou ++ deux fois
  • ou utilisez std :: advance ()

Je les ai tous deux testés avec VC ++ 7 pour le cas d’arête lorsque l’iterator pointe sur le dernier élément du conteneur STL ou au-delà:

vector vec; vec.push_back( 1 ); vec.push_back( 2 ); vector::iterator it = vec.begin(); advance( it, 2 ); bool isAtEnd = it == vec.end(); // true it++; // or advance( it, 1 ); - doesn't matter isAtEnd = it == vec.end(); //false it = vec.begin(); advance( it, 3 ); isAtEnd = it == vec.end(); // false 

J’ai vu plusieurs fois un conseil à comparer à vector :: end () lors de la traversée du vecteur et des autres conteneurs:

 for( vector::iterator it = vec.begin(); it != vec.end(); it++ ) { //manipulate the element through the iterator here } 

Évidemment, si l’iterator est avancé au-delà du dernier élément de la boucle, la comparaison dans l’instruction for-loop sera évaluée à false et la boucle continuera heureusement dans un comportement indéfini.

Ai-je raison de dire que si jamais j’utilise la méthode advance () ou toute opération d’incrémentation sur un iterator et que cela pointe au-delà de la fin du conteneur, je ne pourrai pas détecter cette situation? Si oui, quelle est la meilleure pratique – ne pas utiliser de telles avancées?

Voici la citation du livre de Nicolai Josuttis:

Notez que advance () ne vérifie pas s’il traverse la fin () d’une séquence (il ne peut pas vérifier car les iterators en général ne connaissent pas les conteneurs sur lesquels ils opèrent). Ainsi, l’appel de cette fonction peut entraîner un comportement indéfini car l’opérateur appelant ++ à la fin d’une séquence n’est pas défini

En d’autres termes, la responsabilité du maintien de l’iterator dans l’intervalle incombe totalement à l’appelant.

Peut-être devriez-vous avoir quelque chose comme ça:

 template  Itr safe_advance(Itr i, Itr end, size_t delta) { while(i != end && delta--) i++; return i; } 

Vous pouvez le surcharger lorsque iterator_category est random_access_iterator pour faire quelque chose comme ceci:

 return (delta > end - i)? end : i + delta; 

Vous pouvez utiliser la fonction “distance” entre votre iterator (it) et l’iterator de vec.begin () et la comparer avec la taille du vecteur (obtenue par size ()).

Dans ce cas, votre boucle devrait ressembler à ceci:

 for (vector::iterator it = vec.begin(); distance(vec.begin(), it) < vec.size(); ++it) { // Possibly advance n times here. } 

Le code que suggère Marijn est juste un peu faux (comme l’a fait remarquer d’utilisateurs curieux).

La version correcte de la dernière ligne est la suivante:

 bool isPastEnd = it >= vec.end(); 

container.end () – l’élément juste après la fin – est la seule valeur extérieure définie.

Un iterator vérifié échouera sur ce qui constitue essentiellement un access hors plage, mais cela n’est pas très utile (d’autant plus que le comportement par défaut consiste à mettre fin au programme).

Je pense que la meilleure pratique est de “ne pas faire ça” – soit vérifier chaque valeur de l’iterator (de préférence dans un élément enveloppé comme un filtre), et opérer uniquement sur des entrées intéressantes, ou utiliser l’index explicitement avec

pour (int i = 0; i

Vous pouvez également faire plus de comparaisons dans votre déclaration:

 for( vector::iterator it = vec.begin(); it != vec.end() && it+1 != vec.end(); it+=2 ) { //manipulate the element through the iterator here } 

Je ne sais pas comment cela fonctionnerait par rapport à la suggestion de Kostas , mais il semble que ce serait mieux pour un petit incrément. Bien sûr, il serait difficile de maintenir un niveau élevé car vous avez besoin d’un chèque pour chacun, mais c’est une autre option.

Je l’éviterais certainement si possible. Si vous devez vraiment incrémenter de 2 valeurs à la fois, envisagez alors d’avoir un vecteur std :: pair ou un vecteur de structure avec 2 éléments.

Je vous suggère de jeter un oeil à Boost.Range .
Il pourrait être plus sûr d’utiliser.
Il sera également en C ++ 0x.

Même si cette question remonte à un an et demi, il peut être utile de mentionner l’utilisation des opérateurs de comparaison> et

 vector vec; vec.push_back( 1 ); vec.push_back( 2 ); vector::iterator it = vec.begin(); it+=10; //equivalent to advance( it, 10 ) bool isPastEnd = it > vec.end(); //true