2009-06-29 4 views
49

Was passiert, wenn ich einen Iterator um 2 inkrementiere, wenn er auf das letzte Element eines Vektors zeigt? In this question zu fragen, wie der Iterator auf einen STL-Container von zwei Elementen zwei verschiedene Ansätze angeboten anzupassen:Was passiert, wenn Sie einen Iterator erhöhen, der dem Enditerator eines STL-Containers entspricht?

  • entweder verwenden, um eine Form von arithmetischen Operator - + = 2 oder ++ zweimal
  • oder verwenden std :: Voraus()

ich habe beide mit VC++ 7 für den Rand Fall untersucht, wenn der Iterator Punkte auf das letzte Element der STL-Container oder darüber hinaus:

vector<int> vec; 
vec.push_back(1); 
vec.push_back(2); 

vector<int>::iterator it = vec.begin(); 
advance(it, 2); 
bool isAtEnd = it == vec.end(); // true 
it++; // or advance(it, 1); - doesn't matter 
isAtEnd = it == vec.end(); //false 
it = vec.begin(); 
advance(it, 3); 
isAtEnd = it == vec.end(); // false 

Ich habe may mal eine Advise gesehen gegen Vektor vergleichen :: end(), wenn Sie den Vektor und andere Behältnisse durchlaufen:

for(vector<int>::iterator it = vec.begin(); it != vec.end(); it++) { 
    //manipulate the element through the iterator here 
} 

Natürlich, wenn der Iterator nach dem letzten Element in der Schleife fortgeschritten ist der Vergleich in der Die for-loop-Anweisung wird als false ausgewertet, und die Schleife wird glücklich in undefiniertes Verhalten fortgesetzt.

Habe ich richtig verstanden, dass ich diese Situation nicht erkennen kann, wenn ich advance() oder irgendeine Inkrementierungsoperation für einen Iterator verwende und auf das Ende des Containers zeigen lasse? Wenn ja, was ist die beste Vorgehensweise - solche Fortschritte nicht zu verwenden?

Antwort

50

Es folgt das Zitat von Nicolai Josuttis Buch:

Beachten Sie, dass Fortschritt() nicht nicht überprüft, ob es sich um das Ende() einer Sequenz kreuzt (es kann t überprüfen, weil Iteratoren im Allgemeinen die Behälter nicht kennen, auf denen sie operieren). So Aufruf diese Funktion könnte Ergebnis in undefiniertem Verhalten, weil rufenden Operator ++ für das Ende einer Sequenz nicht

Mit anderen Worten definiert ist, die Verantwortung für den Iterator im Bereich der Aufrechterhaltung liegt völlig mit der Anrufer.

+2

Wie kann man überprüfen, ob es in dem Bereich nach Rechenoperation ist? – Narek

+6

Das ist überhaupt nicht überzeugend. Wo ist das Zitat vom Standard? Ich kann Josuttis Behauptung im Moment nicht bestätigen. :( –

1

container.end() - das Element kurz nach dem Ende - ist der einzige definierte äußere Wert.

Ein überprüfter Iterator wird einen Fehler verursachen, der im Wesentlichen außerhalb des Bereichs liegt, aber das ist nicht besonders hilfreich (vor allem, da das Standardverhalten das Beenden des Programms ist).

Ich denke, die beste Praxis ist "tue das nicht" - entweder überprüfen Sie jeden Wert des Iterators (vorzugsweise in etwas als Filter verpackt), und nur auf interessante Einträge oder verwenden Sie den Index explizit mit

for (int i = 0; i < vec.size(); i + = 2) {...}

10

Vielleicht sollten Sie etwas davon haben:

template <typename Itr> 
Itr safe_advance(Itr i, Itr end, size_t delta) 
{ 
    while(i != end && delta--) 
     i++; 
    return i; 
} 

Sie diese überlasten für wenn iterator_category<Itr> ist random_access_iterator zu tun etwa wie folgt:

return (delta > end - i)? end : i + delta; 
6

Sie könnten die Funktion "distance" zwischen Ihrem Iterator (it) und dem Iterator bei vec.begin() verwenden und diese mit der Vektorgröße (erhalten durch size()) vergleichen.

In diesem Fall Ihre for-Schleife würde wie folgt aussehen:

for (vector<int>::iterator it = vec.begin(); distance(vec.begin(), it) < vec.size(); ++it) 
{ 
    // Possibly advance n times here. 
} 
+0

Ist dies auf zusammenhängende Speicherzuweisung angewiesen oder wird es auch für andere Container funktionieren? – sharptooth

+2

Es hat mehr damit zu tun, wie Operator ++ für jeden Typ von Iterator implementiert ist. "Abstand" zählt, wie viele ++ oder - würde müssen zu einem Iterator ausgeführt werden, um den anderen zu erreichen. Wenn ich Sie wäre, würde ich es auf alle Arten von Container versuchen, die ich erwarte und sehen, welche Entfernung mir gibt. – Kostas

+1

distance() funktioniert auf jedem Eingang, vorwärts, Bidirektionaler oder Random-Access-Iterator, nach dem Standard (24.3.4) – jalf

1

Sie auch weitere Vergleiche tun könnten in Ihrer für Aussage:

for(vector<int>::iterator it = vec.begin(); it != vec.end() && it+1 != vec.end(); it+=2) { 
    //manipulate the element through the iterator here 
} 

Ich weiß nicht, wie dies vs führen würde Kostas's Vorschlag, aber es fühlt wie es wäre besser für ein kleines Inkrement. Natürlich wäre es für ein großes Inkrement ziemlich unhaltbar, da Sie für jedes eine Überprüfung benötigen, aber es ist eine andere Option.

Ich würde es auf jeden Fall vermeiden, wenn überhaupt möglich. Wenn Sie wirklich um 2 Werte gleichzeitig erhöhen müssen, dann überlegen Sie sich, einen Vektor von std :: pair oder einen Vektor einer Struktur mit 2 Elementen zu haben.

1

Ich empfehle Ihnen, einen Blick auf Boost.Range zu werfen.
Es könnte sicherer sein zu verwenden.
Es wird auch in C++ 0x sein.

-1

Obwohl diese Frage ein halbes Jahr alt ist, könnte es immer noch nützlich sein, die Verwendung von Vergleichsoperatoren> und < zu erwähnen, um zu überprüfen, ob Sie über das Ende (oder den Anfang der Iteration) des Containers iteriert haben. Zum Beispiel:

vector<int> vec; 
vec.push_back(1); 
vec.push_back(2); 

vector<int>::iterator it = vec.begin(); 

it+=10; //equivalent to advance(it, 10) 
bool isPastEnd = it > vec.end(); //true 
+0

Entschuldigung, das ist falsch. Wie zuvor erläutert, kann ein gültiger Iterator nicht über den Iterator für eine einzige Nachfolgeebene hinausgehen, dh end(). – curiousguy

+0

Das funktioniert nur, weil die Implementierung von std :: vector :: iterator eigentlich ein roher Zeiger ist. Mit einem anderen Container kann die Geschichte komplett anders sein, es kann sogar eine Ausnahme auslösen, wenn Sie versuchen, über das Ende hinauszugehen. – Sogartar

2

Der Code, Marijn schlägt nur etwas falsch ist (wie curiousguy darauf hingewiesen).

Die korrekte Version der letzten Zeile ist:

bool isPastEnd = it >= vec.end(); 
+0

'it> vec.end()' kann immer falsch angenommen werden. '++ vec.end()' ist undefiniertes Verhalten – Caleth