Reenviar iteradores en C++

Después de pasar por la definición de plantilla de varios algoritmos STL como std::search , std::search_n , std::lower_bound , debe haber encontrado su definición de plantilla que consta de objetos del tipo Iterador hacia adelante . Entonces, ¿qué son y por qué se utilizan?

Los iteradores directos son uno de los cinco tipos principales de iteradores presentes en la biblioteca estándar de C++, otros son iteradores de entrada, iterador de salida , iterador bidireccional e iteradores de acceso aleatorio .

Se considera que los iteradores directos son la combinación de iteradores de entrada y de salida . Da soporte a la funcionalidad de ambos. Permite tanto acceder como modificar valores.

Una cosa importante a tener en cuenta es que los iteradores bidireccionales y de acceso aleatorio también son iteradores directos válidos, como se muestra en la jerarquía de iteradores anterior.

Características sobresalientes

  1. Usabilidad: realizar operaciones en un iterador hacia adelante que es desreferenciable nunca hace que su valor de iterador sea no desreferenciable, como resultado, esto permite que los algoritmos que usan esta categoría de iteradores usen varias copias de un iterador para pasar más de una vez por los mismos valores de iterador. Por lo tanto, se puede utilizar en algoritmos de múltiples pasos .
  2. Comparación de igualdad/desigualdad: un iterador directo se puede comparar para la igualdad con otro iterador. Dado que los iteradores apuntan a alguna ubicación, los dos iteradores serán iguales solo cuando apunten a la misma posición, de lo contrario no.

    Entonces, las siguientes dos expresiones son válidas si A y B son iteradores directos:

    A == B  // Checking for equality
    A != B  // Checking for inequality
    
  3. Eliminación de referencias: debido a que se puede eliminar la referencia de un iterador de entrada, el uso del operador * y -> como un valor r y un iterador de salida se pueden eliminar como un valor l, por lo que los iteradores directos se pueden usar para ambos fines.

    // C++ program to demonstrate forward 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 values to locations pointed by iterator
            *i1 = 1;
        }
      
        for (i1 = v1.begin(); i1 != v1.end(); ++i1) {
            // Accessing values at locations pointed by iterator
            cout << (*i1) << " ";
        }
      
        return 0;
    }

    Producción:

    1 1 1 1 1
    

    Entonces, como podemos ver aquí, podemos acceder y asignar valor al iterador, por lo tanto, el iterador es al menos un iterador hacia adelante (también puede tener una jerarquía más alta).

  4. Incrementable: un iterador directo se puede incrementar, de modo que se refiera al siguiente elemento en secuencia, usando el operador ++().

    Nota: El hecho de que podamos usar iteradores directos con el operador de incremento no significa que el operador – -() también se pueda usar con ellos. Recuerde que los iteradores de avance son unidireccionales y solo pueden moverse en la dirección de avance.

    Entonces, las siguientes dos expresiones son válidas si A es un iterador directo:

    A++   // Using post increment operator
    ++A   // Using pre increment operator
    
  5. Intercambiable: el valor al que apuntan estos iteradores se puede intercambiar o intercambiar.

Implementación práctica

Después de comprender sus características, es muy importante conocer también su implementación práctica. Como se dijo anteriormente, los iteradores directos se pueden usar tanto cuando queremos acceder a elementos como cuando tenemos que asignarles elementos, ya que es la combinación de iterador de entrada y salida. Los siguientes dos algoritmos STL pueden mostrar este hecho:

  • std::replace: como sabemos, este algoritmo se usa para reemplazar todos los elementos en el rango que son iguales a un valor particular por un nuevo valor. Entonces, veamos su funcionamiento interno (no entre en detalles, solo mire dónde se pueden usar los iteradores directos y dónde no):

    // Definition of std::replace()
    template void replace(ForwardIterator first, ForwardIterator last,
                          const T& old_value, const T& new_value)
    {
        while (first != last) {
            if (*first == old_value) // L1
                *first = new_value; // L2
            ++first;
        }
    }

    Aquí, podemos ver que hemos hecho uso de iteradores directos, ya que necesitamos hacer uso de la función de iteradores tanto de entrada como de salida. En L1, se requiere que primero eliminemos la referencia del iterador como un valor r (iterador de entrada) y en L2, se nos requiere que primero eliminemos la referencia como un valor l (iterador de salida), por lo tanto, para lograr ambas tareas, hemos hecho uso de iteradores directos .

  • std::reverse_copy: como sugiere el nombre, este algoritmo se usa para copiar un rango en otro rango, pero en orden inverso. Ahora, en lo que respecta al acceso a los elementos y la asignación de elementos, los iteradores directos están bien, pero tan pronto como tengamos que disminuir el iterador, entonces no podemos usar estos iteradores directos para este propósito.

    // Definition of std::reverse_copy()
    template OutputIterator reverse_copy(BidirectionalIterator first,
                                         BidirectionalIterator last,
                                         OutputIterator result)
    {
        while (first != last)
            *result++ = *--last;
        return result;
    }

    Aquí, podemos ver que hemos declarado último como un iterador bidireccional, y no un iterador hacia adelante, ya que no podemos disminuir un iterador hacia adelante como se hizo en el caso de último, por lo que no podemos usarlo en este escenario.

Nota: como sabemos, los iteradores directos son la combinación de iteradores de entrada y salida, por lo que si algún algoritmo implica el uso de cualquiera de estos dos iteradores, también podemos usar iteradores directos en su lugar, sin afectar el programa.

Por lo tanto, los dos ejemplos anteriores muestran muy bien cuándo, dónde, por qué y cómo se usan los iteradores directos en la práctica.

Limitaciones

Después de estudiar las características más destacadas, también se deben conocer sus deficiencias, aunque no hay tantas como en los iteradores de entrada o salida, ya que está más arriba en la jerarquía.

  1. No se puede decrementar: al igual que podemos usar el operador ++() con iteradores directos para incrementarlos, no podemos decrementarlos. Aunque está más arriba en la jerarquía que los iteradores de entrada y salida, todavía no puede superar esta deficiencia.

    Por eso, su nombre es adelante, lo que demuestra que solo puede moverse en dirección hacia adelante .

    If A is a forward iterator, then
    
    A--    // Not allowed with forward iterators
    
  2. Operadores relacionales: aunque los iteradores directos se pueden usar con el operador de igualdad (==), no se pueden usar con otros operadores relacionales como =.
    If A and B are forward iterators, then
    
    A == B     // Allowed
    A <= B     // Not Allowed
    
  3. 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 avance solo pueden moverse en una dirección demasiado hacia adelante y demasiado secuencialmente.
    If A and B are forward iterators, then
    
    A + 1     // Not allowed
    B - 2     // Not allowed
    
  4. Uso del operador de desreferencia de desplazamiento ([ ]): los iteradores directos no admiten el operador de desreferencia de desplazamiento ([ ]), que se utiliza para el acceso aleatorio.
    If A is a forward iterator, then
    A[3]    // 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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *