2009-06-29 19 views
49

¿Qué sucede si incrementó un iterador entre 2 cuando apunta al último elemento de un vector? En this question preguntando cómo ajustar el iterador a un contenedor STL por 2 elementos se ofrecen dos enfoques diferentes:¿Qué ocurre si incrementa un iterador que es igual al iterador final de un contenedor de STL

  • o bien utilizar una forma de operador aritmético - + ++ = 2 ó dos veces
  • o utilizar std :: avance()

he probado ambos con VC++ 7 para el caso de borde cuando los puntos de iterador en el último elemento del contenedor STL o más allá:

vector<int> vec; 
vec.push_back(1); 
vec.push_back(2); 

vector<int>::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 

he visto veces al día Puede un consejo para comparar contra el vector :: end() cuando se atraviesa el vector y otros recipientes:

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

Obviamente, si el iterador está avanzado más allá del último elemento en el interior del bucle de la comparación en el La sentencia for-loop evaluará a false y el bucle continuará felizmente en un comportamiento indefinido.

¿Lo entiendo bien si alguna vez uso el avance() o cualquier tipo de operación de incremento en un iterador y hago que apunte más allá del final del contenedor no podré detectar esta situación? Si es así, ¿cuál es la mejor práctica, no usar tales avances?

Respuesta

50

A continuación se presenta la cita del libro Nicolai Josuttis:

Tenga en cuenta que avance() no comprueba si se cruza el final() de una secuencia (que puede 't comprobar porque los iteradores en general no conocen los contenedores en los que operan). Por lo tanto, llamar a esta función podría resultado un comportamiento indefinido porque operador de llamada ++ para el final de una secuencia no se define

En otras palabras, la responsabilidad de mantener el iterador dentro de la gama se encuentra totalmente con llamador.

+2

Cómo comprobar si está en el rango después de la operación aritmética? – Narek

+6

Esto no es para nada convincente. ¿Dónde está la cita del estándar? No puedo respaldar la afirmación de Josuttis en este momento. :( –

1

container.end() - el elemento justo después del final - es el único valor exterior definido.

Un iterador verificado tendrá fallas en lo que esencialmente es un acceso fuera de rango, pero eso no es terriblemente útil (especialmente porque el comportamiento predeterminado es finalizar el programa).

Creo que la mejor práctica es "no hagas eso" - o bien comprobar cada valor del iterador (preferiblemente en algo envuelto como un filtro), y sólo operan en las entradas interesantes, o utilizar el índice de forma explícita con

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

10

Tal vez debería tener algo como esto:

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

usted puede sobrecargar este para cuando iterator_category<Itr> es random_access_iterator para hacer algo como lo siguiente:

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

Puede usar la función "distancia" entre su iterador (it) y el iterador en vec.begin() y compararla con el tamaño del vector (obtenido por tamaño()).

En ese caso, el bucle se vería así:

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

¿Este depender de la asignación de almacenamiento contiguo o va a trabajar también para otros recipientes – sharptooth

+2

Es más tiene que ver con la forma del operador ++ se implementa para cada tipo de iterador recuento de "distancia" cuántos ++ o -?. Habría tiene que ser ejecutado en un iterador para llegar al otro. Si yo fuera usted lo probaría en todos los tipos de contenedores que espero y veré qué distancia me da. – Kostas

+1

distancia() funciona en cualquier entrada, adelante, El iterador bidireccional o de acceso aleatorio, de acuerdo con la norma (24.3.4) – jalf

1

También podría hacer más comparaciones en su sentencia for:

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

No sé cómo podría realizar vs Kostas's sugerencia, pero se siente como sería mejor para un pequeño incremento. Por supuesto, sería bastante imposible de mantener para un gran incremento ya que necesita un cheque para cada uno, pero es otra opción.

, sin duda evitarlo si es posible. Si realmente necesita aumentar en 2 valores a la vez, considere tener un vector de std :: pair o un vector de una estructura con 2 elementos.

1

Le sugiero que eche un vistazo a Boost.Range.
Podría ser más seguro de usar.
También estará en C++ 0x.

-1

Aunque esta pregunta tiene medio año de antigüedad, podría ser útil mencionar el uso de operadores de comparación> y < para verificar si se repitió el final (o el inicio al iterar) del contenedor. Por ejemplo:

vector<int> vec; 
vec.push_back(1); 
vec.push_back(2); 

vector<int>::iterator it = vec.begin(); 

it+=10; //equivalent to advance(it, 10) 
bool isPastEnd = it > vec.end(); //true 
+0

Lo siento, eso es INCORRECTO. Como se explicó anteriormente, un iterador válido no puede pasar el iterador one-end-end, que es end(). – curiousguy

+0

Esto solo funciona porque la implementación de std :: vector :: iterator es en realidad un puntero sin formato. Con otro contenedor, la historia puede ser completamente diferente, incluso puede arrojar una excepción cuando intenta pasar de largo. – Sogartar

2

El código que sugiere Marijn es simplemente incorrecto (como curiosoguy señaló).

La versión correcta de la última línea es:

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

'it> vec.end()' se puede suponer que siempre será falso. '++ vec.end()' es un comportamiento indefinido – Caleth

Cuestiones relacionadas