Potrzebujesz iteratora, gdy używasz pętli for opartych na dystansie


84

Obecnie mogę wykonywać tylko pętle dystansowe za pomocą tego:

for (auto& value : values)

Ale czasami potrzebuję iteratora do wartości zamiast odniesienia (z dowolnego powodu). Czy jest jakaś metoda bez konieczności porównywania wartości przez cały wektor?

Odpowiedzi:


77

Użyj starej forpętli jako:

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

Dzięki temu jesteś valuerównie dobrze jak iterator it. Użyj tego, czego chcesz.


EDYTOWAĆ:

Chociaż nie polecałbym tego, ale jeśli chcesz użyć forpętli opartej na zakresie (tak, z jakiegoś powodu : D), możesz to zrobić:

 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
 }

To podejście unika poszukiwań podane value, ponieważ valuei itsą zawsze zsynchronizowane.


Tak, właśnie to robię. Zastanawiałem się tylko, czy zamiast tego jest rozwiązanie z pętlami dystansowymi
小 太郎

4
Zgadzam się, że pierwsze rozwiązanie ze starą pętlą for jest znacznie lepsze: P
小 太郎

@ 小 太郎: Lub możesz użyć, std::findjeśli potrzebujesz zlokalizować wartość ... Stare dobre algorytmy są nadal w nowym standardzie.
David Rodríguez - dribeas

1
@David: A jeśli w wektorze są duplikaty? valuei itmoże nie być zsynchronizowany. Pamiętaj valueto odniesienie.
Nawaz,

9
@Nawaz: Myślę, że źle zrozumiałem ostatnie zdanie. Myślałem, że używa zasięgu, dla którego jest używany do zlokalizowania znanego obiektu. BTW, wolą ++it, aby it++w miarę możliwości (zarówno zastosowań w kodzie), jak to może mieć mniejszy narzut.
David Rodríguez - dribeas

15

Oto klasa opakowująca proxy, która umożliwia ujawnienie ukrytego iteratora poprzez aliasowanie go do własnej zmiennej.

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

Stosowanie:

#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

Spróbowałem na tym i znalazłem rozwiązanie.

Stosowanie:

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

Wdrożenie nie było takie trudne:

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, cóż, tak. Nie do końca miałem to do powiedzenia, że ​​kompilator mógł pobrać jego T z konstruktora ... więc pomyślałem o decltype i zobaczyłem wzdęcie użycia ... i nie widziałem, że może uzyskać jego T z funkcji ... szablon funkcji, dzięki. Czy to w porządku, jak mam to teraz zrobić?
ładunek

2
Tak, to wygląda dobrze. FWIW, tam boost::counting_iteratorchociaż, który robi dokładnie to, i jest dogodnie owinięte boost::counting_range, więc można napisać: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo,

1
Myślę, że operator++()powinienem zwrócić InnerIterator, poza tym bardzo miły i nieprzyjemny.
Ben Voigt,

2

forPętla oparta na zakresie jest tworzona jako odpowiednik języka C ++ dla języka foreachJava, co umożliwia łatwą iterację elementów tablicy. Jest przeznaczony do usuwania użycia złożonych struktur, takich jak iteratory, aby uprościć to. Jeśli chcesz iterator, jak powiedział Nawaz, będziesz musiał użyć normalnej forpętli.


Chciałbym, aby oferowali podobną pętlę, która zamiast tego używała iteratorów :(
小 太郎

1
Cieszę się, że to, co otrzymujesz, to wartość, a nie iterator, dla mnie zakres oparty forjest na cukrze składni i zmniejszeniu ilości wpisywanych znaków. Konieczność wyłuskiwania iteratora sprawi, że będzie on podatny na błędy, szczególnie w połączeniu zauto
TeaOverflow

2

Jest na to bardzo prosty sposób std::vector , który powinien również działać, jeśli zmieniasz rozmiar wektora w trakcie procesu (nie jestem pewien, czy zaakceptowana odpowiedź dotyczy tego przypadku)

Jeśli bto twój wektor, możesz to zrobić

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

gdzie iter będzie wymagany iterator.

Wykorzystuje to fakt, że wektory C ++ są zawsze ciągłe .


2
Jeśli już wykorzystujesz fakt, że wektory C ++ są ciągłe, równie dobrze możesz wykorzystać fakt, że każda rozsądna implementacja będzie po prostu wpisywać f vector<T>::iteratordo T*: Sprawdź to za pomocą a static_assert(), a następnie po prostu użyj T* iter = &i;.
cmaster

1

Zróbmy to bardzo brudne ... Wiem, że 0x70h zmienia się wraz z użyciem stosu, wersją kompilatora ... Powinien być ujawniony przez kompilator, ale tak nie jest :-(

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

1
Nie mam słów, to jest złe na tak wielu poziomach, że nawet nie wiedziałbym, od czego zacząć to krytykować.
swineone
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.