Después de pasar por la definición de plantilla de varios algoritmos STL como std::copy , std::move , std::transform , debe haber encontrado su definición de plantilla que consta de objetos de tipo Iterador de salida . Entonces, ¿qué son y por qué se utilizan?
Los iteradores de salida son uno de los cinco tipos principales de iteradores presentes en la biblioteca estándar de C++, otros son iteradores de entrada , iterador directo , iterador bidireccional e iteradores de acceso aleatorio .
Los iteradores de salida se consideran exactamente lo contrario de los iteradores de entrada, ya que realizan la función opuesta a los iteradores de entrada. Se les pueden asignar valores en una secuencia , pero no se pueden usar para acceder a los valores, a diferencia de los iteradores de entrada que hacen lo contrario de acceder a los valores y no se les pueden asignar valores. Entonces, podemos decir que los iteradores de entrada y salida son complementarios entre sí.
Una cosa importante a tener en cuenta es que los iteradores directos , bidireccionales y de acceso aleatorio también son iteradores de salida válidos, como se muestra en la jerarquía de iteradores anterior.
Características sobresalientes
- Usabilidad: al igual que los iteradores de entrada, los iteradores de salida solo se pueden usar con algoritmos de un solo paso , es decir, algoritmos en los que podemos ir a todas las ubicaciones en el rango como máximo una vez, de modo que estas ubicaciones se pueden desreferenciar o asignar valor solo una vez. .
- Comparación de igualdad/desigualdad: a diferencia de los iteradores de entrada, los iteradores de salida no se pueden comparar en cuanto a la igualdad con otro iterador.
Por lo tanto, las siguientes dos expresiones no son válidas si A y B son iteradores de salida:
A == B // Invalid - Checking for equality A != B // Invalid - Checking for inequality
- Eliminación de referencias: se puede eliminar la referencia de un iterador de entrada como un valor r, utilizando el operador * y ->, mientras que se puede eliminar la referencia de un iterador de salida como un valor l para proporcionar la ubicación para almacenar el valor.
Entonces, las siguientes dos expresiones son válidas si A es un iterador de salida:
*A = 1 // Dereferencing using * A -> m = 7 // Assigning a member element m
- Incrementable: un iterador de salida se puede incrementar, de modo que se refiera al siguiente elemento en secuencia, usando el operador ++().
Entonces, las siguientes dos expresiones son válidas si A es un iterador de salida:
A++ // Using post increment operator ++A // Using pre increment operator
- Intercambiable: el valor al que apuntan estos iteradores se puede intercambiar o intercambiar.
Implementación práctica
Después de comprender sus características y deficiencias, es muy importante conocer también su implementación práctica. Como se dijo anteriormente, los iteradores de salida se usan solo cuando queremos asignar elementos y no cuando tenemos que acceder a los elementos. Los siguientes dos algoritmos STL pueden mostrar este hecho:
- std::move: como sugiere el nombre, este algoritmo se utiliza para mover elementos de un rango a otro rango. Ahora, en lo que se refiere a acceder a los elementos, los iteradores de entrada están bien, pero tan pronto como tenemos que asignar elementos en otro contenedor, no podemos usar estos iteradores de entrada para este propósito, es por eso que aquí usar iteradores de salida se convierte en una compulsión.
// Definition of std::move() template OutputIterator move (InputIterator first, InputIterator last, OutputIterator result) { while (first!=last) { *result = std::move(*first); ++result; ++first; } return result; }
Aquí, dado que el resultado es el iterador del contenedor resultante, al que se asignan los elementos, para esto no podemos usar iteradores de entrada y hemos usado iteradores de salida en su lugar, mientras que para acceder a los elementos, se usan iteradores de entrada que solo necesita ser incrementado y accedido.
- std::find: como sabemos, este algoritmo se usa para encontrar la presencia de un elemento dentro de un contenedor y no implica el uso de iteradores de salida.
// Definition of std::find() template InputIterator find (InputIterator first, InputIterator last, const T& val) { while (first!=last) { if (*first==val) return first; ++first; } return last; }
Entonces, dado que aquí, no había necesidad de asignar valores a los iteradores y solo necesitábamos acceder y comparar los iteradores, por lo que no había necesidad de un iterador de salida y, por lo tanto, solo usamos los iteradores de entrada.
Entonces, los dos ejemplos anteriores muestran muy bien cuándo, dónde, por qué y cómo se usan los iteradores de salida en la práctica.
Limitaciones
Después de estudiar las características más destacadas, también se deben conocer las deficiencias de los iteradores de salida, que se mencionan en los siguientes puntos:
- Solo asignación, sin acceso: una de las mayores deficiencias es que no podemos acceder a los iteradores de salida como rvalue. Por lo tanto, un iterador de salida solo puede modificar el elemento al que apunta si se usa como destino para una asignación.
// C++ program to demonstrate output iterator
#include<iostream>
#include<vector>
using
namespace
std;
int
main()
{
vector<
int
>v1 = {1, 2, 3, 4, 5};
// Declaring an iterator
vector<
int
>::iterator i1;
for
(i1=v1.begin();i1!=v1.end();++i1)
{
// Assigning elements using iterator
*i1 = 1;
}
// v1 becomes 1 1 1 1 1
return
0;
}
Sin embargo, lo anterior es un ejemplo de asignación de elementos mediante el iterador de salida, si hacemos algo como:
a = *i1 ; // where a is a variable
Por lo tanto, esto no está permitido para el iterador de salida, ya que solo pueden ser el objetivo en la asignación. Sin embargo, si intenta esto para el código anterior, funcionará, porque los vectores devuelven iteradores más altos en la jerarquía que los iteradores de salida.
Esta gran deficiencia es la razón por la que muchos algoritmos como std::find , que requieren acceder a los elementos en un rango y verificar la igualdad, no pueden usar iteradores de salida para hacerlo, porque no podemos acceder a los valores usándolo, así que en su lugar hacemos uso de iteradores de entrada.
- No se puede decrementar: al igual que podemos usar el operador ++() con iteradores de salida para incrementarlos, no podemos decrementarlos.
If A is an output iterator,then A-- // Not allowed with output iterators
- Uso en algoritmos de múltiples pasos: dado que es unidireccional y solo puede avanzar, por lo tanto, dichos iteradores no se pueden usar en algoritmos de múltiples pasos, en los que necesitamos movernos a través del contenedor varias veces.
- Operadores relacionales: al igual que los iteradores de salida no se pueden usar con operadores de igualdad (==), tampoco se pueden usar con otros operadores relacionales como =.
If A and B are output iterators, then A == B // Not Allowed A <= B // Not Allowed
- Operadores aritméticos: al igual que los operadores relacionales, tampoco se pueden usar con operadores aritméticos como +, –, etc. Esto significa que los operadores de salida solo pueden moverse en una dirección demasiado hacia adelante y demasiado secuencialmente.
If A and B are output iterators, then A + 1 // Not allowed B - 2 // Not allowed
Este artículo es una contribución de Mrigendra Singh . Si le gusta GeeksforGeeks y le gustaría contribuir, también puede escribir un artículo usando contribuya.geeksforgeeks.org o envíe su artículo por correo a contribuya@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
Publicación traducida automáticamente
Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA