6 Frage: Benötigen Sie einen Iterator, wenn Sie for-Schleifen auf der Basis eines Bereichs verwenden

Frage erstellt am Fri, Jan 24, 2014 12:00 AM

Gegenwärtig kann ich nur auf Distanz basierende Schleifen mit folgendem Befehl ausführen:

 
for (auto& value : values)

Manchmal benötige ich jedoch einen Iterator für den Wert anstelle einer Referenz (aus welchem ​​Grund auch immer). Gibt es eine Methode, bei der nicht der gesamte Vektor verglichen werden muss?

    
79
6 Antworten                              6                         

Verwenden Sie die alte for-Schleife als:

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

Damit haben Sie value sowie Iterator it. Verwenden Sie alles, was Sie verwenden möchten.


BEARBEITEN:

Ich würde dies zwar nicht empfehlen, aber wenn Sie eine bereichsbasierte for-Schleife 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
 }

Bei diesem Ansatz wird die Suche nach value vermieden, da value und it immer synchron sind.

    
70
2017-12-06 16: 07: 54Z
  1. Ja, das habe ich getan. Ich habe mich nur gefragt, ob es stattdessen eine Lösung mit ferngesteuerten Schleifen gibt
    2011-08-05 07: 59: 36Z
  2. @ 小 太郎: Siehe Bearbeiten.
    2011-08-05 08: 07: 25Z
  3. Ich stimme zu, dass die erste Lösung mit der alten for-Schleife viel besser ist: P
    2011-08-05 08: 09: 32Z
  4. @ David: Was ist, wenn der Vektor Duplikate enthält? value und it sind möglicherweise nicht synchron. Denken Sie daran, value ist eine Referenz.
    2011-08-05 09: 13: 45Z
  5. @ Nawaz: Ich glaube, ich habe den letzten Satz falsch verstanden. Ich dachte, dass er den Bereich für die Suche nach einem bekannten Objekt verwendet. Übrigens, ziehen Sie ++it nach Möglichkeit it++ vor (beides wird in Ihrem Code verwendet), da es möglicherweise einen geringeren Overhead hat.
    2011-08-05 10: 14: 01Z

Hier ist eine Proxy-Wrapper-Klasse, mit der Sie den verborgenen Iterator anzeigen können, indem Sie ihn als Alias ​​für Ihre eigene Variable verwenden.

 
#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';
}
    
14
2015-08-12 02: 23: 14Z
  1. + 1 zum Experimentieren mit Wrapper. : -)
    2011-08-05 10: 27: 15Z

Ich habe es selbst 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 Implementierung 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);
}
    
10
2013-02-17 14: 55: 28Z
  1. Bitte verwenden Sie eine Funktionsvorlage für den ForIterator ...
    2013-02-17 12: 56: 09Z
  2. Ah ja. Ich habe nicht ganz verstanden, dass der Compiler sein T vom Konstruktor bekommen kann ... also habe ich an decltype gedacht und den Usage-Bloat gesehen ... und ich habe nicht gesehen, dass es sein T von einer Funktion bekommen kann ... Funktionsvorlage, danke. Ist es richtig, wie ich es jetzt mache?
    2013-02-17 14: 57: 44Z
  3. YDas sieht gut aus. FWIW, es gibt boost::counting_iterator, das genau das macht, und es ist bequem mit boost::counting_range verpackt, so dass Sie schreiben können: for(auto it : boost::counting_range(r.begin(), r.end())). :)
    2013-02-17 15: 10: 29Z
  4. Ich denke, operator++() sollte eine InnerIterator zurückgeben, ansonsten sehr nett und uesful.
    2013-12-14 17: 09: 38Z
Die

bereichsbasierte for-Schleife wird als c ++ - Gegenstück für foreach in Java erstellt und ermöglicht die einfache Iteration von Array-Elementen. Es ist dafür gedacht, die Verwendung komplexer Strukturen wie Iteratoren zu entfernen, um es einfacher zu machen. Wenn Sie eine iterator wollen, müssen Sie, wie Nawaz sagte, eine normale for-Schleife verwenden.

    
4
2011-08-05 08: 15: 13Z
  1. Ich wünschte, sie würden eine ähnliche Schleife anbieten, die stattdessen Iteratoren verwendet: (
    2011-08-05 08: 17: 50Z
  2. Ich bin froh, dass Sie den Wert und nicht den Iterator erhalten, denn für mich basiert der Bereich for auf der Syntax sugar und geht es darum, die Anzahl der Eingaben zu verringern. Wenn der Iterator dereferenziert werden muss, ist er fehleranfällig, insbesondere wenn er mit auto
    verwendet wird
    2012-05-29 18: 19: 51Z

Für std::vector gibt es eine sehr einfache Methode, 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 b Ihr Vektor ist, können Sie dies einfach tun

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

Dabei ist iter Ihr erforderlicher Iterator.

Dies macht sich die Tatsache zunutze, dass C ++ - Vektoren immer zusammenhängend sind .

    
1
2017-05-23 12: 34: 33Z
  1. Wenn Sie bereits die Tatsache ausnutzen, dass C ++ - Vektoren zusammenhängend sind, können Sie auch die Tatsache ausnutzen, dass bei jeder vernünftigen Implementierung nur vector<T>::iterator bis T* eingegeben werden: Überprüfen Sie dies mit a static_assert(), dann einfach T* iter = &i; verwenden.
    2017-04-15 07: 43: 00Z

Lass es uns sehr dreckig machen ... Ich weiß, die 0x70h ändert sich mit Stack-Nutzung, Compiler-Version, .... Es 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;
}
    
1
2018-02-09 09: 13: 30Z
Quelle platziert Hier