(Inspiriert von einem Kommentar von nakiya)
Viele STL-Algorithmen verwenden einen Bereich als ein Paar von Iteratoren. Zum Beispiel for_each(begin, end, &foo);
. Offensichtlich, wenn distance(begin, end) >= N
und begin ein Random-Access-Iterator ist, wendet for_each(begin, begin+N, &foo);
foo
nur auf die ersten N Elemente an.
Gibt es jetzt eine saubere, generische Alternative, wenn eine dieser beiden Bedingungen nicht erfüllt ist?
Es gibt keine generische vollständige Lösung, ohne den Iteratortyp zu ändern.
Beweis: Angenommen, der Iteratortyp ist nur ein InputIterator, so bezieht sich begin
beispielsweise auf einen Stream und end
ist ein Spezialfallmarker-Iterator, der mit dem "echten" Iterator, sobald der reale Iterator EOF gelesen hat.
Dann wird jede Verwendung von begin
, um zu versuchen, einen neuen Wert von end
auszuarbeiten, an den Algorithmus zu übergeben, den ursprünglichen Wert von begin
"konsumieren", da InputIterators so funktioniert.
Sie könnten eine Iterator-Wrapper-Klasse schreiben, so dass der Iterator zählt, wie oft er inkrementiert wurde, und nach einem N-maligen Inkrementieren mit einem "Ende" -Iterator verglichen wird. N könnte ein Vorlagenparameter oder ein Konstruktorparameter für den einen oder anderen Iterator sein.
So ähnlich. Ich habe es getestet kompiliert und arbeitet für mich. Noch zu tun - ich behandle derzeit nur eine Ihrer beiden Situationen, "kein Random-Access-Iterator". Ich handle auch nicht mit dem anderen, "distance & lt; N".
%Vor%Beachten Sie, dass der zweite Konstruktor nur einen Iterator verwendet, da der zugrunde liegende Typ möglicherweise nicht standardmäßig konstruierbar ist (wenn es nur ein InputIterator ist). Wenn der Aufrufer einen End-Iterator erstellt, wird er nicht verwendet, da er nicht gültig ist, wenn die andere Kopie inkrementiert wird.
Wenn der zugrunde liegende Iteratortyp RandomAccess ist, wird dieser Wrapper nicht benötigt / gesucht. Also stelle ich eine Hilfsvorlagenfunktion zur Verfügung, die den Typabzug auf die gleiche Weise wie back_inserter
für back_insert_iterator
durchführt. Wenn der Parametertyp jedoch ein Iterator der zufälligen Zugriffskategorie ist, sollte der Helper nicht FiniteIterator<T>
, sondern nur T
:
Beispielverwendung (und minimaler Test):
%Vor% Vorsicht: finite_iterator(x, 1) == finite_iterator(++x, 0)
ist falsch , auch für einen Vorwärtsiterator oder besser. Finite Iteratoren sind nur vergleichbar, wenn sie vom selben Startpunkt aus erstellt werden.
Auch das ist noch nicht vollständig. Zum Beispiel funktioniert std::reverse
nicht, weil finite_iterator(x, 1)
für den Zugriff auf den Verweis "auf" x zeigt.
Derzeit passiert Folgendes:
%Vor%Ich bin also nicht weit weg, aber das ist keine gute Schnittstelle. Ich müsste mehr über den Fall von bidirektionalen Iteratoren nachdenken.
Es gibt bereits fill_n und generate_n, es gibt kein foreach_n (oder for_n wäre wahrscheinlich geeigneter), aber es ist einfach genug, eins zu schreiben.
%Vor%Sie könnten op (* begin ++) ausführen, aber obwohl es weniger tippt, erzeugt es möglicherweise mehr Code, um den Iterator zu kopieren. size_type ist numerisch, also ist Post-Increment nicht weniger effizient und hier ist ein Fall, in dem es nützlich ist.
Ich glaube, Sie könnten einen Wrapper-Iterator-Typ ähnlich wie boost::counting_iterator
erstellen, der sowohl ein Inkrement als auch den zugrundeliegenden Iterator zusammenhält und gleich einem "Ende" -Iterator vergleicht, sobald das Inkrement den Maximalwert überschreitet.