it-swarm.com.de

Was ist der effektivste Weg, um den Index eines Iterators eines std :: -Vektors zu erhalten?

Ich durchlaufe einen Vektor und benötige den Index, auf den der Iterator gerade zeigt. AFAIK dies kann auf zwei Arten erfolgen:

  • it - vec.begin()
  • std::distance(vec.begin(), it)

Was sind die Vor- und Nachteile dieser Methoden?

398
cairol

Ich würde it - vec.begin() genau aus dem von Naveen angegebenen entgegengesetzten Grund bevorzugen: also würde nicht kompilieren, wenn Sie den Vektor in eine Liste ändern. Wenn Sie dies während jeder Iteration tun, können Sie leicht einen O(n) Algorithmus in einen O (n ^ 2) Algorithmus verwandeln.

Eine andere Möglichkeit, wenn Sie während der Iteration nicht im Container herumspringen, besteht darin, den Index als zweiten Schleifenzähler beizubehalten.

Hinweis: it ist ein gebräuchlicher Name für einen Container-Iterator, std::container_type::iterator it;.

507
UncleBens

Ich würde std::distance(vec.begin(), it) vorziehen, da ich den Container ohne Codeänderungen ändern kann. Wenn Sie sich zum Beispiel für std::list anstatt std::vector, das keinen Iterator mit wahlfreiem Zugriff bietet, wird Ihr Code dennoch kompiliert. Da std :: distance je nach Iteratormerkmalen die optimale Methode auswählt, kommt es auch nicht zu Leistungseinbußen.

124
Naveen

Wie UncleBens und Naveen gezeigt haben, gibt es für beide gute Gründe. Welches "besser" ist, hängt von Ihrem gewünschten Verhalten ab: Möchten Sie ein zeitlich konstantes Verhalten garantieren oder möchten Sie, dass es bei Bedarf auf die lineare Zeit zurückgreift?

it - vec.begin() benötigt eine konstante Zeit, aber operator - ist nur für Iteratoren mit wahlfreiem Zugriff definiert, sodass der Code beispielsweise mit Listeniteratoren überhaupt nicht kompiliert werden kann.

std::distance(vec.begin(), it) funktioniert für alle Iteratortypen, ist jedoch nur dann eine Operation mit konstanter Zeit, wenn sie für Iteratoren mit wahlfreiem Zugriff verwendet wird.

Keiner ist "besser". Verwenden Sie das, was Sie brauchen.

71
jalf

Ich mag dieses: it - vec.begin(), weil für mich klar "Abstand vom Anfang" steht. Bei Iteratoren sind wir es gewohnt, arithmetisch zu denken, daher ist das - - Zeichen hier der klarste Indikator.

11
Eli Bendersky

Wenn Sie Ihren Algorithmus bereits auf die Verwendung eines std::vector::iterator und std::vector::iterator nur, es ist nicht wirklich wichtig, welche Methode Sie verwenden werden. Ihr Algorithmus ist bereits über den Punkt hinaus konkretisiert, an dem die Auswahl eines anderen den Unterschied ausmachen kann. Beide machen genau dasselbe. Es ist nur eine Frage der persönlichen Präferenz. Ich persönlich würde explizite Subtraktion verwenden.

Wenn Sie andererseits einen höheren Grad an Allgemeingültigkeit in Ihrem Algorithmus beibehalten möchten, um zu ermöglichen, dass er eines Tages auf einen anderen Iteratortyp angewendet wird, hängt die beste Methode von Ihrer Absicht ab . Es hängt davon ab, wie restriktiv Sie in Bezug auf den Iteratortyp sein möchten, der hier verwendet werden kann.

  • Wenn Sie die explizite Subtraktion verwenden, wird Ihr Algorithmus auf eine ziemlich enge Klasse von Iteratoren beschränkt: Iteratoren mit wahlfreiem Zugriff. (Das bekommen Sie jetzt von std::vector)

  • Wenn Sie distance verwenden, unterstützt Ihr Algorithmus eine viel größere Klasse von Iteratoren: Eingabe-Iteratoren.

Natürlich ist die Berechnung von distance für Iteratoren ohne wahlfreien Zugriff im Allgemeinen eine ineffiziente Operation (während sie für Iteratoren mit wahlfreiem Zugriff ebenso effizient ist wie die Subtraktion). Es liegt an Ihnen, zu entscheiden, ob Ihr Algorithmus sinnvoll für Iteratoren ohne wahlfreien Zugriff, was die Effizienz anbelangt. Wenn der resultierende Effizienzverlust so verheerend ist, dass Ihr Algorithmus völlig unbrauchbar wird, sollten Sie sich besser an die Subtraktion halten, um ineffiziente Verwendungen zu verhindern und den Benutzer zu zwingen, nach alternativen Lösungen für andere Iteratortypen zu suchen. Wenn die Effizienz mit Iteratoren ohne wahlfreien Zugriff immer noch im nutzbaren Bereich liegt, sollten Sie distance verwenden und dokumentieren, dass der Algorithmus mit Iteratoren mit wahlfreiem Zugriff besser funktioniert.

7
AnT

Laut http://www.cplusplus.com/reference/std/iterator/distance/ ist vec.begin() ein zufälliger Zugriff Iterator, die Distanzmethode verwendet den Operator -.

Aus Sicht der Leistung ist die Antwort also dieselbe, aber vielleicht ist die Verwendung von distance() einfacher zu verstehen, wenn jemand Ihren Code lesen und verstehen müsste.

4
Stéphane

Ich würde die - - Variante nur für std::vector Verwenden - es ist ziemlich klar, was damit gemeint ist, und die Einfachheit der Operation (die nicht mehr als eine Zeigersubtraktion ist) wird durch die Syntax ausgedrückt (distance dagegen klingt in der ersten Lesung wie Pythagoras, oder?) Wie UncleBen betont, fungiert - Auch als statische Behauptung, falls vector versehentlich in list geändert wird.

Ich denke auch, dass es viel häufiger ist - ich habe jedoch keine Zahlen, die das beweisen könnten. Hauptargument: it - vec.begin() ist im Quellcode kürzer - weniger Schreibarbeit, weniger Platzbedarf. Da es klar ist, dass die richtige Antwort auf Ihre Frage eine Geschmackssache ist, kann dies auch ein gültiges Argument sein.

3