È necessario un iteratore quando si utilizzano i cicli for basati su intervalli


85

Al momento, posso eseguire loop basati su intervalli solo con questo:

for (auto& value : values)

Ma a volte ho bisogno di un iteratore per il valore, invece di un riferimento (per qualsiasi motivo). Esiste un metodo senza dover passare attraverso l'intero vettore confrontando i valori?

Risposte:


78

Usa il vecchio forloop come:

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

Con questo, hai valueanche iteratore it. Usa quello che vuoi usare.


MODIFICARE:

Anche se non lo consiglierei, ma se vuoi usare il forciclo basato su intervallo (sì, per qualsiasi motivo : D), allora puoi farlo:

 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
 }

Questo approccio evita la ricerca data value, poiché valuee itsono sempre sincronizzati.


Sì, questo è quello che ho fatto. Mi stavo solo chiedendo se ci fosse una soluzione con i loop basati su intervalli invece
小 太郎

4
Sono d'accordo che la prima soluzione con il vecchio ciclo for sia molto migliore: P
小 太郎

@ 小 太郎: Oppure potresti usarlo std::findse ciò di cui hai bisogno è individuare un valore ... I buoni vecchi algoritmi sono ancora nel nuovo standard.
David Rodríguez - dribeas

1
@ David: cosa succede se ci sono duplicati nel vettore? valuee itpotrebbe non essere sincronizzato. Ricorda valueè un riferimento.
Nawaz

9
@Nawaz: Penso di aver frainteso l'ultima frase. Ho pensato che stesse usando la distanza basata su per individuare un oggetto noto. BTW, preferiscono ++ita it++quando possibile (entrambi gli usi nel codice), in quanto potrebbe avere un overhead minore.
David Rodríguez - dribeas

15

Ecco una classe wrapper proxy che ti consente di esporre l'iteratore nascosto assegnandogli un alias alla tua variabile.

#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 ); }

Utilizzo:

#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';
}

13

Ci ho provato e ho trovato una soluzione.

Utilizzo:

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

L'implementazione non è stata così difficile:

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, beh sì. Non ho capito bene che il compilatore poteva ottenere la sua T dal costruttore ... quindi ho pensato a decltype e ho visto l'utilizzo-bloat ... e non ho visto che può ottenere la sua T da una funzione ... modello di funzione, grazie. È giusto, come lo faccio adesso?
carico utile

2
Sì, sembra buono. FWIW, c'è boost::counting_iteratorperò, che fa esattamente questo, ed è convenientemente avvolto con boost::counting_range, in modo da poter scrivere: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo

1
Penso che operator++()dovrebbe restituire un InnerIterator, altrimenti molto carino e utile.
Ben Voigt

2

il for ciclo basato su intervallo viene creato come controparte c ++ per foreachin java che consente una facile iterazione degli elementi dell'array. È pensato per rimuovere l'uso di strutture complesse come gli iteratori in modo da renderlo semplice. Voglio un iterator, come ha detto Nawaz, dovrai usare un forciclo normale .


Vorrei che offrissero un ciclo simile che usasse invece iteratori, però :(
小 太郎

1
Sono felice che quello che stai ottenendo è il loro valore e non l'iteratore, perché per me l'intervallo basato forè lo zucchero di sintassi e sulla riduzione della quantità di digitazione. Dover dereferenziare l'iteratore lo renderebbe soggetto a errori, specialmente se usato conauto
TeaOverflow,

2

C'è un modo molto semplice per farlo std::vector, che dovrebbe funzionare anche se stai ridimensionando il vettore durante il processo (non sono sicuro che la risposta accettata consideri questo caso)

Se bè il tuo vettore, puoi semplicemente farlo

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

dove itersarà l'iteratore richiesto.

Ciò si avvale del fatto che i vettori C ++ sono sempre contigui .


2
Se si sta già sfruttando il fatto che C ++ vettori sono contigui, si potrebbe anche sfruttare anche il fatto che qualsiasi applicazione sano di mente sarà solo typedef vector<T>::iteratora T*: Verificare che con una static_assert(), poi basta usare T* iter = &i;.
cmaster - ripristina monica il

1

Facciamolo molto sporco ... Lo so, 0x70h sta cambiando con l'utilizzo dello stack, la versione del compilatore, ... Dovrebbe essere esposto dal compilatore, ma non lo è :-(

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

1
Non ho parole, questo è sbagliato in così tanti livelli, non saprei nemmeno da dove iniziare a criticarlo.
swineone
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.