Begrenzen Sie STL-Algorithmen auf N Elemente

8

(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?

    
MSalters 11.11.2010, 11:44
quelle

3 Antworten

9

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 :

zurückgeben %Vor%

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.

    
Steve Jessop 11.11.2010, 12:27
quelle
5

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.

    
CashCow 11.11.2010 12:21
quelle
0

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.

    
Victor Nicollet 11.11.2010 11:58
quelle

Tags und Links