Necesita iterador cuando se utilizan bucles for basados ​​en rangos

84

Actualmente, solo puedo hacer bucles a distancia con esto:

for (auto& value : values)

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

小 太郎
fuente

Respuestas:

77

Utilice el forciclo antiguo como:

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

Con esto, tienes valueademás de iterador it. Usa lo que quieras usar.


EDITAR:

Aunque no recomendaría esto, pero si desea usar un forbucle basado en rango (sí, por el motivo que sea : D), puede 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 buscar dados value, ya que valuey itsiempre están sincronizados.

Nawaz
fuente
Sí, esto es lo que he estado haciendo. Me preguntaba si había una solución con bucles basados ​​en rangos en su lugar
小 太郎
4
Estoy de acuerdo en que la primera solución con el antiguo bucle for es mucho mejor: P
小 太郎
@ 小 太郎: O puede usarlo std::findsi lo que necesita es localizar un valor ... Los buenos algoritmos antiguos todavía están en el nuevo estándar.
David Rodríguez - dribeas
1
@David: ¿Qué pasa si hay duplicados en el vector? valuey itpuede que no esté sincronizado. Recuerde valuees una referencia.
Nawaz
9
@Nawaz: Creo que entendí mal la última oración. Pensé que estaba usando el rango basado en para localizar un objeto conocido. Por cierto, prefiera siempre ++itque it++sea ​​posible (ambos usos en su código), ya que podría tener una sobrecarga menor.
David Rodríguez - dribeas
15

Aquí hay una clase contenedora de proxy que le permite exponer el iterador oculto asignándolo 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';
}
Potatoswatter
fuente
13

Me probé 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 difícil:

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);
}
carga útil
fuente
ah, bueno si. No entendí bien que el compilador podría obtener su T del constructor ... así que pensé en decltype y vi el hinchamiento de uso ... y no vi que pudiera obtener su T de una función ... plantilla de función, gracias. ¿Está bien, cómo lo hago ahora?
carga útil
2
Sí, eso se ve bien. Fwiw, hay boost::counting_iteratorsin embargo, que hace exactamente eso, además de estar envuelto con boost::counting_range, por lo que puede escribir: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo
1
Creo que operator++()debería devolver un InnerIterator, por lo demás muy agradable y uesful.
Ben Voigt
2

El for bucle basado en rango se crea como la contraparte de c ++ foreachen 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. Si quieres un iterator, como dijo Nawaz, tendrás que usar un forbucle normal .

Ragesh Chakkadath
fuente
Sin embargo, desearía que ofrecieran un bucle similar que usara iteradores :(
小 太郎
1
Estoy feliz de que lo que está obteniendo sea el valor y no el iterador, porque para mí, el rango se basa en el forazúcar de sintaxis y en la reducción de la cantidad de escritura. Tener que desreferenciar el iterador lo haría propenso a errores, especialmente cuando se usa conauto
TeaOverflow
2

Hay una forma muy simple de hacer esto std::vector, que 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 bes tu vector, puedes hacer

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

donde iterestará su iterador requerido.

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

chorro de pulso
fuente
2
Si ya está explotando el hecho de que los vectores C ++ son contiguos, también podría explotar el hecho de que cualquier implementación sensata simplemente escribirá def vector<T>::iteratorpara T*: Verifique eso con a static_assert(), luego simplemente use T* iter = &i;.
cmaster - reinstalar a monica
1

Hagámoslo muy sucio ... Lo sé, el 0x70h está cambiando con el uso de la pila, la versión del compilador, ... El compilador debería exponerlo, pero no lo es :-(

char* uRBP = 0; __asm { mov uRBP, rbp }
Iterator** __pBegin = (Iterator**)(uRBP+0x70);
for (auto& oEntry : *this) {
    if (oEntry == *pVal) return (*__pBegin)->iPos;
}
mbusch
fuente
1
No tengo palabras, esto está mal en muchos niveles, ni siquiera sabría por dónde empezar a criticarlo.
swineone