Pregunta Necesita iterador cuando usa bucles basados ​​en rangos


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

for (auto& value : values)

Pero a veces necesito un iterador para el 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?


75
2017-08-05 07:53


origen


Respuestas:


Usa el viejo for lazo como:

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

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


EDITAR:

Aunque no lo recomendaría, pero si quiere usar el rango for loop (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 value, ya que value y it están siempre sincronizados.


64
2017-08-05 07:56



Aquí hay una clase de derivador proxy que le permite exponer el iterador oculto allisá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';
}

14
2017-08-05 09:44



Me probé con 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);
}

10
2018-02-17 11:32



rango basado  for loop se crea como la contraparte de C ++ para foreach en Java que permite una fácil iteración de elementos de la matriz. Está destinado a eliminar el uso de estructuras complejas como iteradores para simplificarlo. Yo quieres un iterator, como dijo Nawaz, deberás usar normal for lazo.


3
2017-08-05 08:15



Hay una manera muy simple de hacer esto para std::vector, lo 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 b es tu vector, 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 C ++ son siempre contiguos.


1
2018-04-15 06:51



Vamos a hacerlo muy sucio ... Lo sé, el 0x70h está cambiando con el uso de la pila, la versión del compilador, ... Debería estar expuesto por el compilador, pero no es :-(

char* uRBP = 0; __asm { mov uRBP, rbp }
Iterator** __pBegin = (Iterator**)(uRBP+0x70);
for (auto& oEntry : *this) {
    if (oEntry == *pVal) return (*__pBegin)->iPos;
}

0
2018-02-09 09:13