Kann ich einen Reverse-Iterator in einen Forward-Iterator umwandeln?

Lesezeit: 8 Minuten

Benutzer-Avatar
BeeBand

Ich habe eine Klasse namens Actionwas im Wesentlichen ein Wrapper um eine Deque von ist Move Objekte.

Weil ich die Deque von durchqueren muss Moves Sowohl vorwärts als auch rückwärts habe ich einen Forward-Iterator und einen Reverse_iterator als Member-Variablen der Klasse. Der Grund dafür ist, dass ich wissen muss, wann ich das “Ende” der Deque überschritten habe, sowohl wenn ich vorwärts als auch rückwärts gehe.

Die Klasse sieht so aus:

class Action
{
public:
    SetMoves(std::deque<Move> & dmoves) { _moves = dmoves; }
    void Advance();
    bool Finished() 
    {
        if( bForward )
            return (currentfwd==_moves.end());
        else
            return (currentbck==_moves.rend());
    }
private:
    std::deque<Move> _moves;
    std::deque<Move>::const_iterator currentfwd;
    std::deque<Move>::const_reverse_iterator currentbck;
    bool bForward;
};

Das Advance Funktion ist wie folgt:

void Action::Advance
{
    if( bForward)
        currentfwd++;
    else
        currentbck++;
}

Mein Problem ist, ich möchte in der Lage sein, einen Iterator zum aktuellen abzurufen Move Objekt, ohne abfragen zu müssen, ob ich vorwärts oder rückwärts gehe. Dies bedeutet, dass eine Funktion einen Iteratortyp zurückgibt, aber ich habe zwei Typen.

Sollte ich vergessen, einen Iterator zurückzugeben, und eine konstante Referenz auf a zurückgeben Move Objekt statt?

  • Die Antwort auf Ihre Frage “Kann ich einen reverse_iterator von einem Forward-Iterator bekommen” lautet ja und hier

    – bobobobo

    2. März 2013 um 17:59 Uhr

Benutzer-Avatar
Mike Seymour

Reverse-Iteratoren haben ein Mitglied base() die einen entsprechenden Forward-Iterator zurückgibt. Beachten Sie, dass dies ist nicht ein Iterator, der auf dasselbe Objekt verweist – er verweist tatsächlich auf das nächste Objekt in der Sequenz. Das ist so rbegin() Korrespondiert mit end() und rend() Korrespondiert mit begin().

Wenn Sie also einen Iterator zurückgeben möchten, würden Sie so etwas tun

std::deque<Move>::const_iterator Current() const
{
    if (forward)
        return currentfwd;
    else
        return (currentbck+1).base();
}

Ich würde es jedoch vorziehen, eine Referenz zurückzugeben und alle Iterationsdetails in der Klasse zu kapseln.

  • (currentbck+1).base() borks, wenn currentbck ein End-Iterator ist. Die Konvertierung zwischen den beiden ist eine Welt voller Fehler, die darauf warten, passiert zu werden.

    – Steve Jessop

    10. Januar 2010 um 18:30 Uhr

Benutzer-Avatar
Jerry Sarg

Das ist exakt die Art von Problem, die das Design von STL veranlasste, damit zu beginnen. Es gibt echte Gründe dafür:

  1. Iteratoren werden nicht zusammen mit Containern gespeichert
  2. Verwenden von Algorithmen, die beliebige Iteratoren akzeptieren
  3. Algorithmen bewerten ein ganzes Sortiment statt eines einzelnen Artikels auf einmal

Ich vermute, was Sie gerade sehen, ist mehr oder weniger die Spitze des Eisbergs der wirklichen Probleme. Mein Rat wäre, einen Schritt zurückzutreten und anstatt zu fragen, wie man mit den Details des Designs in seiner jetzigen Form umgeht, eine etwas allgemeinere Frage darüber zu stellen, was Sie zu erreichen versuchen und wie Sie dies am besten erreichen Endresultat.

Für diejenigen, denen die Titelfrage in erster Linie am Herzen liegt, ist die Antwort ein stark eingeschränktes „Ja“. Insbesondere hat ein reverse_iterator a base() Mitglied dazu. Etwas problematisch sind allerdings die Qualifikationen.

Um das Problem zu demonstrieren, betrachten Sie Code wie diesen:

#include <iostream>
#include <vector>
#include <iterator>

int main() { 
    int i[] = { 1, 2, 3, 4};
    std::vector<int> numbers(i, i+4);

    std::cout << *numbers.rbegin() << "\n";
    std::cout << *numbers.rbegin().base() << "\n";
    std::cout << *(numbers.rbegin()+1).base() << "\n";

    std::cout << *numbers.rend() << "\n";
    std::cout << *numbers.rend().base() << "\n";
    std::cout << *(numbers.rend()+1).base() << "\n";
}

Wenn Sie dies zu diesem bestimmten Zeitpunkt auf meinem speziellen Computer ausführen, wird die folgende Ausgabe erzeugt:

4
0
4
-1879048016
1
-1879048016

Zusammenfassung: mit rbegin() wir muss Fügen Sie einen hinzu, bevor Sie ihn in einen Forward-Iterator umwandeln, um einen gültigen Iterator zu erhalten – aber mit rend() wir müssen nicht fügen Sie eine hinzu, bevor Sie konvertieren, um einen gültigen Iterator zu erhalten.

Solange Sie verwenden X.rbegin() und X.rend() als Parameter für einen generischen Algorithmus ist das in Ordnung – aber die Erfahrung zeigt, dass die Konvertierung in Forward-Iteratoren oft zu Problemen führt.

Am Ende ist die Antwort für den Hauptteil der Frage (im Gegensatz zum Titel) jedoch ziemlich ähnlich wie oben angegeben: Das Problem ergibt sich aus dem Versuch, ein Objekt zu erstellen, das die Sammlung mit ein paar Iteratoren in dieser Sammlung kombiniert . Beheben Sie dieses Problem, und das ganze Geschäft mit Vorwärts- und Rückwärts-Iteratoren wird strittig.

  • Ich mag deine Antwort. Da könntest du recht haben. Ich bin relativ neu in C++ und der STL. Und was gutes C++-Design ausmacht, ist etwas, das ich nur schwer lernen kann.

    – BeeBand

    10. Januar 2010 um 18:56 Uhr

  • Obwohl diese Antwort BeeBand hilft, beantwortet sie nicht die ursprüngliche Frage für die Nachwelt. Diese Antwort sollte stattdessen ein Kommentar zum ursprünglichen Beitrag sein. Ich würde BeeBand bitten, in Betracht zu ziehen, das Häkchen auf Mike Seymours Antwort zu ändern.

    – Lukasz Wiklendt

    11. September 2012 um 23:46 Uhr

  • @Lukasz: Wenn Sie “die Frage” auf das beschränken, was im Titel steht, haben Sie Recht. Wenn Sie jedoch die gesamte Frage selbst lesen, würde ich (bestenfalls) viel weniger sagen.

    – Jerry Sarg

    12. September 2012 um 3:24 Uhr

  • In vier Jahren würde ich behaupten, dass die Leute diese Frage mehr wegen ihres Titels als wegen ihres Inhalts finden.

    – zneak

    16. April 2014 um 6:04 Uhr

  • Weitere fünf Jahre später und es ist noch mehr so ​​^

    – Apollys unterstützt Monica

    11. Dezember 2019 um 21:05 Uhr

Seit std::deque ist ein Container mit wahlfreiem Zugriff (gleich wie std::vector) sind Sie viel besser dran, einen einzelnen ganzzahligen Index in der Deque für beide Durchläufe zu verwenden.

  • Danke – genau aus diesem Grund habe ich im Rest der App Deques verwendet. Ich weiß nicht, warum ich einen Tunnelblick über Iteratoren hatte 🙂

    – BeeBand

    10. Januar 2010 um 18:06 Uhr

  • Deshalb braucht man immer ein zweites Paar Augen 🙂

    – Nikolai Fetissov

    10. Januar 2010 um 18:10 Uhr

  • Aber Vorsicht: Es ist ziemlich schwierig, eine vorzeichenlose Ganzzahl zu testen, um zu wissen, ob sie einen Wert unter Null erreicht hat;)

    – Matthias M.

    10. Januar 2010 um 18:15 Uhr

  • Sie könnten einen (Vorwärts-)Iterator verwenden und darauf achten, ihn nicht zu erhöhen, wenn er gleich end() ist, oder ihn zu verringern, wenn er gleich end() ist. Achten Sie in jedem Fall auf Operationen, die Iteratoren ungültig machen, da sie auch einen Index ungültig machen könnten (entweder weil er nicht mehr auf das Element zeigt, von dem Sie glauben, dass es das tut, oder weil Sie etwas entfernen, wenn der Index auf das Ende der Deque verweist).

    – Steve Jessop

    10. Januar 2010 um 18:29 Uhr

  • Meinst du nicht dekrementieren, wenn gleich begin()? Das Problem dabei ist, wenn Advance() nicht zum funktionalen Äquivalent von rend() geht, gibt eine Funktion wie GetCurrentMove() begin() zurück, wenn tatsächlich alle Bewegungen verarbeitet wurden.

    – BeeBand

    10. Januar 2010 um 18:35 Uhr


Es scheint mir, dass Sie tatsächlich zwei verschiedene Verhaltensweisen in derselben Klasse haben.

Insbesondere scheint es, dass Sie Ihre Sammlung nur in einer Reihenfolge durchqueren können, andernfalls würden Sie mit der Durchquerung beginnen und dann die ändern bforward Argument, würden Sie am Ende in eine ziemlich seltsame Situation geraten.

Persönlich bin ich dafür, beide Iteratoren offenzulegen (d. h. forward begin, end, rbegin and rend).

Sie könnten auch ein einfaches Iterator-Objekt zurückgeben:

template <class T>
class Iterator
{
public:
  typedef typename T::reference_type reference_type;

  Iterator(T it, T end) : m_it(it), m_end(end) {}

  operator bool() const { return m_it != m_end; }

  reference_type operator*() const { return *m_it; }

  Iterator& operator++() { ++m_it; return *this; }

private:
  T m_it;
  T m_end;
};

template <class T>
Iterator<T> make_iterator(T it, T end) { return Iterator<T>(it,end); }

Dann können Sie einfach dieses einfache Objekt zurückgeben:

class Action
{
public:
  Action(std::deque<Move> const& d): m_deque(d) {} // const& please

  typedef Iterator< std::deque<Move>::iterator > forward_iterator_type;
  typedef Iterator< std::deque<Move>::reverse_iterator > backward_iterator_type;

  forward_iterator_type forward_iterator()
  {
    return make_iterator(m_deque.begin(), m_deque.end());
  }

  backward_iterator_type backward_iterator()
  {
    return make_iterator(m_deque.rbegin(), m_deque.rend());
  }


private:
  std::deque<Move> m_deque;
};

Oder wenn Sie dynamisch zwischen Vorwärts- und Rückwärtsdurchlauf wählen möchten, können Sie Iterator zu einer rein virtuellen Schnittstelle machen und sowohl Vorwärts- als auch Rückwärtsdurchlauf haben.

Aber wirklich, ich sehe nicht wirklich den Sinn, SOWOHL Vorwärts- als auch Rückwärts-Iterator zu speichern, wenn es den Anschein hat, dass Sie nur einen verwenden werden: /

Vielleicht sollten Sie Ihre Behälterwahl überdenken.

Normalerweise müssen Sie keine umgekehrten Iteratoren verwenden, um rückwärts zu gehen.

currentfwd--

wird rückwärts gehen, obwohl es mit Dequeue möglicherweise nicht funktioniert (was Sie vermutlich versucht haben).

Was Sie wirklich tun sollten, ist, Ihre Klasse hier als Dekorateur von Dequeue zu modellieren und Ihre eigenen Aktions-Iteratoren zu implementieren. So würde ich es jedenfalls machen.

  • Danke Karl. Das Problem mit dem Rückwärtsgehen liegt in der Finished()-Funktion – ich muss wissen, wann ich vor dem ersten Element eins bin (dh an “rend()” vorbeigegangen bin).

    – BeeBand

    10. Januar 2010 um 17:58 Uhr

  • Danke Karl. Das Problem mit dem Rückwärtsgehen liegt in der Finished()-Funktion – ich muss wissen, wann ich vor dem ersten Element eins bin (dh an “rend()” vorbeigegangen bin).

    – BeeBand

    10. Januar 2010 um 17:58 Uhr

1013080cookie-checkKann ich einen Reverse-Iterator in einen Forward-Iterator umwandeln?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy