web-dev-qa-db-fra.com

Besoin d'itérateur lors de l'utilisation de boucles à distance pour les boucles

Actuellement, je ne peux faire que des boucles à 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 pour comparer les valeurs?

80
小太郎

Utilisez l'ancienne boucle for comme:

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

Avec cela, vous avez value ainsi que l'itérateur it. Utilisez ce que vous voulez utiliser.


MODIFIER:

Bien que je ne le recommande pas, mais si vous souhaitez utiliser une boucle basée sur une plage for (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 de rechercher value étant donné que value et it sont toujours synchronisés.

71
Nawaz

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

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
}

L'implémentation n'était pas 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);
}
10
payload

basé sur une plagefor la boucle est créée en tant que contrepartie c ++ pour foreach dans Java qui permet une itération facile des éléments du tableau. Elle est destiné à supprimer l'utilisation de structures complexes comme les itérateurs afin de simplifier. Si vous voulez un iterator, comme l'a dit Nawaz, vous devrez utiliser une boucle normale for.

4
Ragesh Chakkadath

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 prenne en compte ce cas)

Si b est votre vecteur, vous pouvez simplement faire

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

iter sera votre itérateur requis.

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

2
PulseJet

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;
}
1
mbusch