cplusplus - list iterator c++
Qué sucede si incrementa un iterador que es igual al iterador final de un contenedor STL (8)
¿Qué pasa si incremento un iterador en 2 cuando apunta al último elemento de un vector? En esta pregunta que pregunta cómo ajustar el iterador a un contenedor STL por 2 elementos, se ofrecen dos enfoques diferentes:
- utilizar una forma de operador aritmético - + = 2 o ++ dos veces
- o use std :: advance ()
He probado ambos con VC ++ 7 para el caso de borde cuando el iterador apunta al ú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 algunas veces un consejo para comparar contra vector :: end () al atravesar el vector y otros contenedores:
for( vector<int>::iterator it = vec.begin(); it != vec.end(); it++ ) {
//manipulate the element through the iterator here
}
Obviamente, si el iterador avanza más allá del último elemento dentro del ciclo, la comparación en la instrucción for-loop se evaluará como falsa y el ciclo continuará felizmente en un comportamiento indefinido.
¿Lo entiendo bien si alguna vez uso advance () 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?
A continuación se encuentra la cita del libro de Nicolai Josuttis:
Tenga en cuenta que advance () no verifica si cruza el final () de una secuencia (no puede verificar porque los iteradores, en general, no conocen los contenedores en los que operan). Por lo tanto, llamar a esta función podría dar como resultado un comportamiento indefinido, ya que llamar al operador ++ para el final de una secuencia no está definido
En otras palabras, la responsabilidad de mantener el iterador dentro del rango recae totalmente en la persona que llama.
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ó más allá del 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
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();
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, su bucle for se vería así:
for (vector<int>::iterator it = vec.begin(); distance(vec.begin(), it) < vec.size(); ++it)
{
// Possibly advance n times here.
}
Tal vez deberías tener algo como esto:
template <typename Itr>
Itr safe_advance(Itr i, Itr end, size_t delta)
{
while(i != end && delta--)
i++;
return i;
}
Puede sobrecargar esto cuando iterator_category<Itr>
es random_access_iterator
para hacer algo como lo siguiente:
return (delta > end - i)? end : i + delta;
También podría hacer más comparaciones en su declaración 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 se llevaría a cabo esto contra Kostas''s sugerencia Kostas''s , pero parece que sería mejor por 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.
Definitivamente lo evitaría si fuera 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.
Te sugiero que Boost.Range un vistazo a Boost.Range .
Puede ser más seguro de usar.
También estará en C ++ 0x.
container.end () - el elemento justo después del final - es el único valor exterior definido.
Un iterador verificado tendrá fallas en lo que es esencialmente 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 hacer eso", ya sea verificar cada valor del iterador (preferiblemente en algo envuelto como un filtro), y solo operar en entradas interesantes, o usar el índice explícitamente con
for (int i = 0; i <vec.size (); i + = 2) {...}