
BeeBand
Ich habe eine Klasse namens Action
was 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?

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.

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:
- Iteratoren werden nicht zusammen mit Containern gespeichert
- Verwenden von Algorithmen, die beliebige Iteratoren akzeptieren
- 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.
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.
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.
10130800cookie-checkKann ich einen Reverse-Iterator in einen Forward-Iterator umwandeln?yes
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