2010-10-22 7 views
12

En el código de ejemplo, a menudo veo código como *it++ para los iteradores de salida. La expresión *it++ realiza una copia de it, incrementa it y luego devuelve la copia que finalmente se desreferencia. Según lo entiendo, hacer una copia de un iterador de salida invalida la fuente. Pero entonces el incremento de it que se realiza después de crear la copia sería ilegal, ¿verdad? ¿Está mi comprensión de los iteradores de salida defectuosa?¿Cómo es * it ++ válido para los iteradores de salida?

+0

¿Por qué la copia de un iterador (salida o de otra manera) invalidarla? No rutinariamente hacemos esto cuando decimos cosas como: 'vector :: iterator it (container.begin());' –

+0

@Adrian: Por ejemplo, la [documentación de SGI] (http: //www.sgi. com/tech/stl/OutputIterator.html) dice: "Solo debe haber una copia activa de un solo Iterator de salida en un momento dado. Es decir: después de crear y usar una copia x de un Iterator de salida y, el iterador de salida original y ya no se debe usar ". Sin embargo, no estoy seguro de qué significa "usar" un iterador de salida en este contexto. ¿Desreferencia? Incrementando? – fredoverflow

+2

Veo la implicación de que después de 'out_iterator a (b);', 'b' podría no ser válido en la documentación de SGI como usted dijo, pero nada en C++ 03 lo insinúa. Pero como tanto la página SGI como C++ 03 dicen explícitamente que la expresión '* it ++ = value' debe ser válida, ese requisito anula cualquier otra preocupación sobre ese uso. – aschepler

Respuesta

6

La expresión hace *it++no (tienen que) hacer una copia del mismo, hace no incrementarlo, etc. Esta expresión es válida sólo por conveniencia, ya que sigue la semántica usual. Solo operator= hace el trabajo real. Por ejemplo, en la implementación de g ++ de ostream_iterator, operator*, operator++ y operator++(int) haga solo una cosa: return *this (en otras palabras, ¡nada!).Podríamos escribir por ejemplo:

it = 1; 
it = 2; 
*it = 3; 
++it = 4; 

En lugar de: *it++ = 1; *it++ = 2; *it++ = 3; *it++ = 4;

+1

Esta es la respuesta que me dio un momento aha, por lo tanto, la estoy seleccionando como la correcta. – fredoverflow

+0

Probablemente debería dejar en claro que el primer ejemplo solo funciona porque conoce la implementación y el segundo ejemplo es la forma correcta de usar un iterador de salida. – Dingo

+0

@Dingo: si entiendo correctamente el estándar, no depende de la implementación sino que está bien definido para 'ostream_iterator'. Entonces, en el caso general no se puede requerir que '* it ++' haga una copia, etc. – rafak

-4

¿No es un iterador solo un puntero? Incrementando, luego desreferenciando simplemente pasa al siguiente elemento.

+1

No, los iteradores no son solo punteros. –

+0

Se pueden desreferenciar, al igual que un puntero, esto puede generar confusión, pero no, no son solo punteros, sino que apuntan a un elemento, entre otras cosas. Aquí hay una implementación de muestra de un iterador: http://www.accu-usa.org/Listings/2000-04-Listing01.html –

+0

Ah, ya veo. Nunca he visto el punto en los iteradores, y nunca los he necesitado. –

1

Los iteradores de salida simplemente no funcionan como iteradores normales y su interfaz se especifica para que puedan usarse en expresiones tipo puntero (*it++ = x) con resultados útiles.

Típicamente, operator*(), operator++() y operator++(int) todo volver *this como referencia y los iteradores de salida tienen una magia operator= que lleva a cabo la operación de salida esperada. Como no se puede leer desde un iterador de salida, no importa el hecho de queetc., no funcione como para otros iteradores.

14

El estándar requiere que *r++ = t funcione para los iteradores de salida (24.1.2). Si no funciona, no es un iterador de salida según la definición del estándar.

Depende de la implementación del iterador asegurarse de que tales declaraciones funcionen correctamente bajo el capó.

La razón por la que no debe conservar varias copias de un iterador de salida es que tiene una semántica de pase único. El iterador solo puede desreferenciarse una vez en cada valor (es decir, debe incrementarse entre cada operación de desreferencia). Una vez que se elimina la referencia de un iterador, no se puede copiar.

Es por eso que *r++ = t funciona. Se realiza una copia del iterador original, se elimina la referencia del iterador original y se incrementa la copia. El iterador original nunca volverá a utilizarse y la copia ya no hará referencia al mismo valor.

+0

¿No quiere decir t = * r ++? –

+0

@Seth No. La pregunta original especificaba iteradores de salida. – Dingo

+1

@Seth: No, '* r ++ = t' es cómo se usa un iterador de salida. A la inversa, ni siquiera es necesario compilar. – aschepler

1

Mirando tu comentario, parece que la mayor parte de la confusión surge de la documentación de SGI, que diría que es un poco engañosa en este punto.

Copiar un iterador de salida no no invalidar el iterador copiado. La verdadera limitación es bastante simple: solo debe desreferenciar un valor determinado del iterador de salida una vez. Sin embargo, tener dos copias a la vez está bien, siempre y cuando solo elimines una de ellas mientras tienen el mismo valor. En un caso como en el que está desreferenciando uno, luego descartando su valor e incrementando el otro, pero solo desreferenciando una vez que se ha producido el incremento, todo está perfectamente bien.

Cuestiones relacionadas