tipos recorrer qué iteradores iterador c++ stl iterator language-lawyer standard-library

c++ - qué - recorrer iterator java



¿Por qué los iteradores deben ser predecibles? (3)

Los iteradores de acceso directo / bidireccional / aleatorio a menudo pueden ser indicadores: históricamente han ayudado a la migración desde el código que utiliza el puntero a los iteradores si la construcción y la inicialización pueden dejarse deslocalizados si esa fuera la forma en que el código era. Obligar a un mayor cambio al por mayor habría frustrado a mucha gente que intentaba migrar el código anterior del uso explícito de los punteros y sobre los iteradores. Cambiarlo ahora rompería mucho código.

Los operadores de entrada y salida a menudo se implementan de forma más elegante con referencias a flujo subyacente u otros objetos de E / S, y las referencias deben inicializarse en la construcción. Por supuesto, las implementaciones podrían verse obligadas a diferir eso, y usar punteros internamente, pero eso seguramente arrastrará a algunas personas por el camino equivocado, pareciendo demasiado "C", por lo que no es sorprendente que el estándar facilite el uso de referencias.

Los iteradores de las categorías de acceso directo , bidireccional y aleatorio deben ser predecibles.

¿Por qué es esto y por qué los operadores de entrada y salida no tienen que ser predecibles?


Se requieren iteradores directos y más fuertes para referirse a una secuencia externa (ver [apuntadores.publicados] / 6 que dice "Si a y b son ambos referenciables, entonces a == b si y solo si *a y *b están vinculados a la mismo objeto. ")

Esto significa que generalmente son solo un mango liviano sobre otra cosa (por ejemplo, un puntero a un elemento o un nodo en un contenedor) y entonces hay pocas razones para no requerir que se puedan construir por defecto (incluso si la construcción predeterminada crea un iterador singular) eso no se puede usar para nada hasta que se le asigne un nuevo valor). Es posible que todos los iteradores directos no patológicos * admitan la construcción predeterminada, y confiar en eso hace que algunos algoritmos sean más fáciles de implementar.

Los iteradores que solo cumplen con el iterador de entrada o los requisitos del iterador de salida (y nada más sólido) pueden contener un estado dentro de ellos que el operator++ modifica y, por lo tanto, es posible que ese estado no se construya por defecto. Ningún algoritmo que solo opera en iteradores de entrada / salida necesita construirlos de manera predeterminada, por lo que no es necesario.

  • detectar el argumento de "no es cierto escocés" aquí;)

referencia para iteradores

Iteradores de entrada / salida:

Iterador de entrada : una vez que se ha incrementado un InputIterator i, todas las copias de su valor anterior pueden ser invalidadas.

Iterador de salida : después de esta operación no se requiere que r sea referenciable y ya no se requiere que ninguna copia del valor anterior de r sea derivable o incrementable.

Si miramos esto, parece claro que estos iteradores están diseñados para ser utilizados en el método más simple posible. Al igual que un índice de matriz o un simple puntero, se usaría para algoritmos de un solo pase. Por lo tanto, realmente no es necesario tener constructores por defecto.

Sin embargo, tenga en cuenta que el hecho de que los constructores por defecto no sean necesarios técnicamente no lo descalifica para implementarlos si lo desea.

Reenviar iteradores:

Este es el primer nivel de iteradores que requieren constructores por defecto, pero ¿por qué?

Hay muchas razones relacionadas con motivos históricos de programación, y creo que todo esto es válido hasta cierto punto. De cierta manera, creo que el comité calculó que entre iterador y randomAccessIterator era necesario implementar la construcción predeterminada y que los iteradores directos parecían la mejor opción.

Sin embargo, hay una buena razón por la cual:

Los iteradores directos admiten algoritmos de pasadas múltiples y, por lo tanto, requieren que las copias del iterador sigan siendo válidas después de que el iterador se haya utilizado / incrementado. Si estas copias son válidas, significa que el algoritmo nos permitirá "guardarlas" en algún lugar. Lo que también significa que el iterador donde los guardamos debe tener un valor predeterminado / inicial.

Considerar:

class MyClass { public: void myFunction(ForwardIterator &i) { //do some code here savedIter = i; //do some code here } private: ForwardIterator savedIter; }

Por definición, esto es válido porque podemos guardar el iterador por una cierta cantidad de tiempo, ya que el requisito es que las copias de este iterador sigan siendo válidas. (Al menos hasta que se destruya la estructura de datos a la que apunta el iterador)

Sin embargo, para que se cree esta clase, ForwardIterator requiere obviamente un constructor predeterminado ...