Nécessite un itérateur lors de l'utilisation de boucles for basées sur la distance

84

Actuellement, je ne peux faire que des boucles basées à distance avec ceci:

for (auto& value : values)

Mais parfois, j'ai besoin d'un itérateur de la valeur, au lieu d'une référence (pour une raison quelconque). Existe-t-il une méthode sans avoir à parcourir tout le vecteur de comparaison des valeurs?

小 太郎
la source

Réponses:

77

Utilisez l'ancienne forboucle comme:

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

Avec cela, vous avez valueainsi qu'un itérateur it. Utilisez tout ce que vous voulez utiliser.


ÉDITER:

Bien que je ne le recommande pas, mais si vous souhaitez utiliser une forboucle basée sur la plage (ouais, pour une raison quelconque : D), vous pouvez le faire:

 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
 }

Cette approche évite les recherches données value, car valueet itsont toujours synchronisées.

Nawaz
la source
Oui, c'est ce que j'ai fait. Je me demandais juste s'il y avait une solution avec des boucles basées à distance à la place
小 太郎
4
Je suis d'accord que la première solution avec l'ancienne boucle for est bien meilleure: P
小 太郎
@ 小 太郎: Ou vous pouvez utiliser std::findsi ce dont vous avez besoin est de localiser une valeur ... Les bons vieux algorithmes sont toujours dans le nouveau standard.
David Rodríguez - dribeas
1
@David: Et s'il y a des doublons dans le vecteur? valueet itpeut ne pas être synchronisé. Remember valueest une référence.
Nawaz
9
@Nawaz: Je pense que j'ai mal compris la dernière phrase. Je pensais qu'il utilisait la plage basée sur pour localiser un objet connu. BTW, préfèrent ++ità it++chaque fois que possible ( les deux utilisations dans votre code) car il pourrait avoir une moindre frais généraux.
David Rodríguez - dribeas
15

Voici une classe de wrapper proxy pour vous permettre d'exposer l'itérateur caché en l'aliasant sur votre propre 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 ); }

Usage:

#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
la source
13

Je me suis essayé et j'ai trouvé une solution.

Usage:

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

La mise en œuvre n'a pas été si 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);
}
charge utile
la source
ah, eh bien oui. Je n'ai pas tout à fait compris que le compilateur pouvait obtenir son T du constructeur ... alors j'ai pensé à decltype et j'ai vu l'utilisation-ballonnement ... et je n'ai pas vu qu'il pouvait obtenir son T d'une fonction ... modèle de fonction, merci. Est-ce bien, comment je le fais maintenant?
charge utile le
2
Ouais, ça a l'air bien. FWIW, il est boost::counting_iteratorbien, ce qui fait exactement cela, et est idéalement enveloppé avec boost::counting_range, de sorte que vous pouvez écrire: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo
1
Je pense qu'il operator++()faudrait retourner un InnerIterator, sinon très agréable et utile.
Ben Voigt
2

La for boucle basée sur la plage est créée en tant que contrepartie c ++ pour foreachen java qui permet une itération facile des éléments du tableau. Il est destiné à supprimer l'utilisation de structures complexes comme les itérateurs afin de le rendre simple. Si vous voulez un iterator, comme Nawaz l'a dit, vous devrez utiliser une forboucle normale .

Ragesh Chakkadath
la source
J'aurais aimé qu'ils offrent une boucle similaire utilisant des itérateurs à la place, cependant :(
小 太郎
1
Je suis heureux que vous obteniez de la valeur et non l'itérateur, car pour moi, la plage forest basée sur le sucre de syntaxe et la réduction de la quantité de frappe. Le fait de devoir déréférencer l'itérateur le rendrait sujet aux erreurs, en particulier lorsqu'il est utilisé avecauto
TeaOverflow
2

Il existe un moyen très simple de le faire pour std::vector, qui devrait également fonctionner si vous redimensionnez le vecteur pendant le processus (je ne suis pas sûr que la réponse acceptée considère ce cas)

Si bc'est votre vecteur, vous pouvez simplement faire

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

itersera votre itérateur requis.

Cela tire parti du fait que les vecteurs C ++ sont toujours contigus .

jet d'impulsion
la source
2
Si vous exploitez déjà le fait que les vecteurs C ++ sont contigus, vous pouvez également exploiter le fait que toute implémentation sensée se contentera de taperef vector<T>::iteratorà T*: Vérifiez cela avec a static_assert(), puis utilisez simplement T* iter = &i;.
cmaster - réintégrer monica le
1

Faisons-le très sale ... Je sais, le 0x70h change avec l'utilisation de la pile, la version du compilateur, .... Il devrait être exposé par le compilateur, mais ce n'est pas :-(

char* uRBP = 0; __asm { mov uRBP, rbp }
Iterator** __pBegin = (Iterator**)(uRBP+0x70);
for (auto& oEntry : *this) {
    if (oEntry == *pVal) return (*__pBegin)->iPos;
}
mbusch
la source
1
Je n'ai pas de mots, c'est faux à tant de niveaux, je ne saurais même pas par où commencer à le critiquer.
swineone le