Necesita un iterador cuando se usan bucles for basados ​​en rango

7 minutos de lectura

Avatar de usuario de 小太郎
小太郎

Actualmente, solo puedo hacer bucles basados ​​​​en rangos con esto:

for (auto& value : values)

Pero a veces necesito un iterador del valor, en lugar de una referencia (por el motivo que sea). ¿Hay algún método sin tener que pasar por todo el vector comparando valores?

Avatar de usuario de Nawaz
Nawaz

Usa el viejo for bucle como:

for (auto it = values.begin(); it != values.end();  ++it )
{
       auto & value = *it;
       //...
}

Con esto, has value así como iterador it. Usa lo que quieras usar.


EDITAR:

Aunque no recomendaría esto, pero si desea utilizar el rango basado en for bucle (sí, Por cualquier razón :D), entonces puedes hacer esto:

 auto it = std::begin(values); //std::begin is a free function in C++11
 for (auto& value : values)
 {
     //Use value or it - whatever you need!
     //...
     ++it; //at the end OR make sure you do this in each iteration
 }

Este enfoque evita la búsqueda dada valuedesde value y it siempre están sincronizados.

  • Sí, esto es lo que he estado haciendo. Me preguntaba si había una solución con bucles basados ​​​​en rangos en su lugar

    – 小太郎

    5 de agosto de 2011 a las 7:59

  • Estoy de acuerdo en que la primera solución con el antiguo ciclo for es mucho mejor: P

    – 小太郎

    5 de agosto de 2011 a las 8:09

  • @David: ¿Qué pasa si hay duplicados en el vector? value y it puede no estar sincronizado. Recordar value es una referencia

    – Nawaz

    5 de agosto de 2011 a las 9:13


  • @Nawaz: Creo que no entendí bien la última oración. Pensé que estaba usando el rango basado en para localizar un objeto conocido. Por cierto, prefiero ++it a it++ siempre que sea posible (ambos usos en su código), ya que podría tener una sobrecarga menor.

    – David Rodríguez – dribeas

    5 de agosto de 2011 a las 10:14

  • @David: Escribí it++ como el viejo hábito rara vez va. Pero gracias por recordar esto.

    – Nawaz

    5 de agosto de 2011 a las 10:24

Avatar de usuario de Potatoswatter
matapatatas

Aquí hay una clase contenedora de proxy que le permite exponer el iterador oculto asignándole un alias a su propia variable.

#include <memory>
#include <iterator>

/*  Only provides the bare minimum to support range-based for loops.
    Since the internal iterator of a range-based for is inaccessible,
    there is no point in more functionality here. */
template< typename iter >
struct range_iterator_reference_wrapper
    : std::reference_wrapper< iter > {
    iter &operator++() { return ++ this->get(); }
    decltype( * std::declval< iter >() ) operator*() { return * this->get(); }
    range_iterator_reference_wrapper( iter &in )
        : std::reference_wrapper< iter >( in ) {}
    friend bool operator!= ( range_iterator_reference_wrapper const &l,
                             range_iterator_reference_wrapper const &r )
        { return l.get() != r.get(); }
};

namespace unpolluted {
    /*  Cannot call unqualified free functions begin() and end() from 
        within a class with members begin() and end() without this hack. */
    template< typename u >
    auto b( u &c ) -> decltype( begin( c ) ) { return begin( c ); }
    template< typename u >
    auto e( u &c ) -> decltype( end( c ) ) { return end( c ); }
}

template< typename iter >
struct range_proxy {
    range_proxy( iter &in_first, iter in_last )
        : first( in_first ), last( in_last ) {}

    template< typename T >
    range_proxy( iter &out_first, T &in_container )
        : first( out_first ),
        last( unpolluted::e( in_container ) ) {
        out_first = unpolluted::b( in_container );
    }

    range_iterator_reference_wrapper< iter > begin() const
        { return first; }
    range_iterator_reference_wrapper< iter > end()
        { return last; }

    iter &first;
    iter last;
};

template< typename iter >
range_proxy< iter > visible_range( iter &in_first, iter in_last )
    { return range_proxy< iter >( in_first, in_last ); }

template< typename iter, typename container >
range_proxy< iter > visible_range( iter &first, container &in_container )
    { return range_proxy< iter >( first, in_container ); }

Uso:

#include <vector>
#include <iostream>
std::vector< int > values{ 1, 3, 9 };

int main() {
    // Either provide one iterator to see it through the whole container...
    std::vector< int >::iterator i;
    for ( auto &value : visible_range( i, values ) )
        std::cout << "# " << i - values.begin() << " = " << ++ value << '\n';

    // ... or two iterators to see the first incremented up to the second.
    auto j = values.begin(), end = values.end();
    for ( auto &value : visible_range( j, end ) )
        std::cout << "# " << j - values.begin() << " = " << ++ value << '\n';
}

avatar de usuario de payload
carga útil

Probé yo mismo en esto y encontré una solución.

Uso:

for(auto i : ForIterator(some_list)) {
    // i is the iterator, which was returned by some_list.begin()
    // might be useful for whatever reason
}

La implementación no fue tan dificil:

template <typename T> struct Iterator {
    T& list;
    typedef decltype(list.begin()) I;

    struct InnerIterator {
        I i;
        InnerIterator(I i) : i(i) {}
        I operator * () { return i; }
        I operator ++ () { return ++i; }
        bool operator != (const InnerIterator& o) { return i != o.i; }
    };

    Iterator(T& list) : list(list) {}
    InnerIterator begin() { return InnerIterator(list.begin()); }
    InnerIterator end() { return InnerIterator(list.end()); }
};
template <typename T> Iterator<T> ForIterator(T& list) {
    return Iterator<T>(list);
}

  • ah pues si. No entendí bien que el compilador podría obtener su T del constructor… así que pensé en decltype y vi el uso excesivo… y no vi que puede obtener su T de una función … plantilla de función, gracias. ¿Es correcto, cómo lo hago ahora?

    – carga útil

    17 de febrero de 2013 a las 14:57

  • Sí, eso se ve bien. FWIW, hay boost::counting_iterator sin embargo, que hace exactamente eso, y está convenientemente envuelto con boost::counting_rangepara que puedas escribir: for(auto it : boost::counting_range(r.begin(), r.end())). 🙂

    – Xeo

    17 de febrero de 2013 a las 15:10

  • Creo operator++() debe devolver un InnerIteratorpor lo demás muy agradable y útil.

    – Ben Voigt

    14 de diciembre de 2013 a las 17:09

avatar de usuario de pulsejet
pulsorreactor

Hay una manera muy simple de hacer esto para std::vectorque también debería funcionar si está cambiando el tamaño del vector durante el proceso (no estoy seguro de si la respuesta aceptada considera este caso)

Si b es tu vector, solo puedes hacer

for(auto &i:b){
    auto iter = b.begin() + (&i-&*(b.begin()));
}

dónde iter será su iterador requerido.

Esto aprovecha el hecho de que los vectores de C++ siempre son contiguos.

Tarde como siempre :), pero ya llegué.

C++20 introduce la sintaxis para el instrucción-inicializador en bucles for basados ​​en rango. Esta inicialización puede ser un declaración simpleo un expresión-declaración. (El borrador de trabajo actual de C++23 también hace posible escribir un tipo-alias-declaración en cambio).

Para un iterador o un índice, simplemente haga algo similar a lo siguiente:

std::vector<int> vec;

for (auto it = vec.begin(); auto& elem: vec) { 
   // ...
   it++;
}

for (int i = 0; auto& elem: vec) {
   // ...
   i++;
}

Esto soluciona el problema de alcance del método de variable externa que mencionó @nawaz.

Para tener en cuenta: las expresiones de ese tipo no se limitan a una sola inicialización, y también hay muchas cosas interesantes que se pueden hacer en línea. Ejemplos:

// This will only be useful for containing a complex typedef's scope inside
// a for-loop, and I would say, is a smell that your typing system is not too
// developed.
for(typedef std::vector<std::vector<int>> Matrix; Matrix& m: container) { 
    // ...
}

// Good old (or rather, very new) one liner.
for(MyType my_instance(x,y,z); auto& elem: my_instance) {
    // ...
}

Avatar de usuario de Ragesh Chakkadath
Ragesh Chakkadath

basado en rango for bucle se crea como la contraparte de C++ para foreach en Java que permite una fácil iteración de los elementos de la matriz. Está destinado a eliminar el uso de estructuras complejas como iteradores para hacerlo simple. yo quiero un iteratorcomo dijo Nawaz, tendrás que usar normal for bucle.

Boost tiene un adaptador de rango muy bueno indexado:

  #include <boost/range/adaptors.hpp>
  std::vector<std::string> list = {"boost", "adaptors", "are", "great"};
  for (auto v: list | boost::adaptors::indexed(1)) {
    printf("%ld: %s\n", v.index(), v.value().c_str());
  }

La salida:

1: boost
2: adaptors
3: are
4: great

Aquí indexamos desde 1 (boost::adaptors::indexed(1)), pero también podríamos indexar fácilmente desde cualquier otro valor.

Es un índice, no un iterador, pero los usos más comunes del iterador son

  • convirtiéndolo en índice para acceder a un elemento en otro vector.
  • informar/regresar a la posición donde sucedió algo.
  • accediendo a algún valor vecino como el anterior.
  • decorar la salida de algún tipo.

Todo esto también se puede hacer con el índice directamente. Desde el otro lado, pasar el iterador desde dentro del ciclo a otro lugar para usarlo exactamente como iterador parece un enfoque bastante complicado.

¿Ha sido útil esta solución?