Benötigen Sie einen Iterator, wenn Sie Fernkampf-basierte for-Schleifen verwenden

84

Derzeit kann ich nur Fernkampf-basierte Schleifen damit ausführen:

for (auto& value : values)

Aber manchmal brauche ich einen Iterator für den Wert anstelle einer Referenz (aus welchem ​​Grund auch immer). Gibt es eine Methode, ohne den gesamten Vektor durchlaufen zu müssen, um Werte zu vergleichen?

小 太郎
quelle

Antworten:

75

Verwenden Sie die alte forSchleife als:

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

Damit haben Sie valueebenso wie Iterator it. Verwenden Sie alles, was Sie verwenden möchten.


BEARBEITEN:

Ich würde dies zwar nicht empfehlen, aber wenn Sie eine bereichsbasierte forSchleife verwenden möchten (ja, aus welchem ​​Grund auch immer : D), können Sie dies tun:

 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
 }

Dieser Ansatz vermeidet eine gegebene Suche value, da valueund itimmer synchron sind.

Nawaz
quelle
Ja, das habe ich getan. Ich habe mich nur gefragt, ob es stattdessen eine Lösung mit Fernkampfschleifen gibt
小 太郎
4
Ich bin damit einverstanden, dass die erste Lösung mit der alten for-Schleife viel besser ist: P
小 太郎
@ 小 太郎: Oder Sie können verwenden, std::findwenn Sie einen Wert suchen müssen ... Gute alte Algorithmen sind immer noch im neuen Standard.
David Rodríguez - Dribeas
1
@ David: Was ist, wenn der Vektor Duplikate enthält? valueund itmöglicherweise nicht synchron. Denken Sie daran, valueist eine Referenz.
Nawaz
9
@Nawaz: Ich glaube, ich habe den letzten Satz falsch verstanden. Ich dachte, dass er den Bereich verwendet, um ein bekanntes Objekt zu lokalisieren. Übrigens, ziehen Sie es vor ++it, it++wann immer möglich (beide Verwendungen in Ihrem Code), da dies möglicherweise einen geringeren Overhead hat.
David Rodríguez - Dribeas
15

Hier ist eine Proxy-Wrapper-Klasse, mit der Sie den verborgenen Iterator durch Aliasing auf Ihre eigene Variable verfügbar machen können.

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

Verwendung:

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

Ich habe es versucht und eine Lösung gefunden.

Verwendung:

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

Die Umsetzung war nicht so schwierig:

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);
}
Nutzlast
quelle
Ah, na ja. Ich habe nicht ganz verstanden, dass der Compiler sein T vom Konstruktor erhalten kann ... also dachte ich an decltype und sah das Aufblähen der Nutzung ... und ich sah nicht, dass es sein T von einer Funktion erhalten kann ... Funktionsvorlage, danke. Ist es richtig, wie ich es jetzt mache?
Nutzlast
2
Ja, das sieht gut aus. FWIW gibt es boost::counting_iteratorjedoch, was genau das tut und bequem verpackt ist boost::counting_range, so dass Sie schreiben können : for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo
1
Ich denke operator++()sollte zurückkehren InnerIterator, sonst sehr nett und uesful.
Ben Voigt
2

Die bereichsbasierte for Schleife wird als C ++ - Gegenstück für foreachJava erstellt, das eine einfache Iteration von Array-Elementen ermöglicht. Es ist dazu gedacht, die Verwendung komplexer Strukturen wie Iteratoren zu entfernen, um es einfach zu machen. Wenn du willst iterator, wie Nawaz sagte, musst du eine normale forSchleife verwenden.

Ragesh Chakkadath
quelle
Ich wünschte, sie würden eine ähnliche Schleife anbieten, die stattdessen Iteratoren verwendet :(
小 太郎
1
Ich bin froh, dass Sie den Wert und nicht den Iterator erhalten, denn für mich basiert der Bereich auf Syntaxzucker und der forReduzierung der Schreibmenge. Wenn der Iterator dereferenziert werden muss, ist er fehleranfällig, insbesondere wenn er mitauto
TeaOverflow
2

Es gibt eine sehr einfache Möglichkeit, dies zu tun std::vector, die auch funktionieren sollte, wenn Sie die Größe des Vektors während des Vorgangs ändern (ich bin nicht sicher, ob die akzeptierte Antwort diesen Fall berücksichtigt).

Wenn bes Ihr Vektor ist, können Sie es einfach tun

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

Wo iterwird Ihr erforderlicher Iterator sein?

Dies nutzt die Tatsache aus, dass C ++ - Vektoren immer zusammenhängend sind .

PulseJet
quelle
2
Wenn Sie bereits nutzen die Tatsache , dass C ++ Vektoren benachbart sind, dann kann man auch ausnutzen auch die Tatsache , dass jeder vernünftige Implementierung wird nur typedef vector<T>::iteratorzu T*: Überprüfen Sie, ob mit ein static_assert(), dann nur verwenden T* iter = &i;.
cmaster - wieder herstellen Monica
0

Lass es uns sehr schmutzig machen ... Ich weiß, die 0x70h ändert sich mit der Stack-Nutzung, der Compiler-Version, .... Sie sollte vom Compiler verfügbar gemacht werden, ist aber nicht :-(

char* uRBP = 0; __asm { mov uRBP, rbp }
Iterator** __pBegin = (Iterator**)(uRBP+0x70);
for (auto& oEntry : *this) {
    if (oEntry == *pVal) return (*__pBegin)->iPos;
}
mbusch
quelle
Ich habe keine Worte, das ist auf so vielen Ebenen falsch, ich würde nicht einmal wissen, wo ich anfangen soll, es zu kritisieren.
Swineone