Es gibt viele nützliche Funktionen in <algorithm>
, aber alle arbeiten mit "Sequenzen" - Paaren von Iteratoren. Wenn ich zB einen Container habe und darauf laufen möchte std::accumulate
, muss ich schreiben:
std::vector<int> myContainer = ...;
int sum = std::accumulate(myContainer.begin(), myContainer.end(), 0);
Wenn alles, was ich vorhabe, ist:
int sum = std::accumulate(myContainer, 0);
Welches ist ein bisschen lesbarer und klarer, in meinen Augen.
Jetzt sehe ich, dass es Fälle geben kann, in denen Sie nur Teile eines Containers bearbeiten möchten. Daher ist es auf jeden Fall nützlich, die Möglichkeit zu haben , Bereiche zu passieren. Aber zumindest nach meiner Erfahrung ist das ein seltener Sonderfall. Normalerweise möchte ich ganze Container bearbeiten.
Es ist einfach , eine Wrapper - Funktion zu schreiben , die einen Behälter und Anrufe nehmen begin()
und end()
auf mich, aber solche Komfortfunktionen sind nicht in der Standard - Bibliothek enthalten.
Ich würde gerne die Gründe für diese Wahl des STL-Designs erfahren.
quelle
boost::accumulate
Antworten:
Es mag nach Ihrer Erfahrung ein seltener Sonderfall sein , aber in Wirklichkeit ist der gesamte Container der Sonderfall, und der willkürliche Bereich ist der allgemeine Fall.
Sie haben bereits bemerkt, dass Sie den gesamten Containerfall über die aktuelle Schnittstelle implementieren können, aber nicht umgekehrt.
Der Bibliotheksschreiber hatte also die Wahl, zwei Schnittstellen im Vorfeld zu implementieren oder nur eine, die noch alle Fälle abdeckt.
Richtig, zumal die kostenlosen Funktionen
std::begin
undstd::end
jetzt enthalten sind.Nehmen wir also an, die Bibliothek bietet die Bequemlichkeitsüberladung:
Jetzt muss auch die äquivalente Überladung bereitgestellt werden, indem ein Vergleichs-Funktor verwendet wird, und wir müssen die Äquivalente für jeden anderen Algorithmus bereitstellen.
Aber wir haben zumindest jeden Fall abgedeckt, in dem wir mit einem vollen Container arbeiten wollen, oder? Nicht ganz. Erwägen
Wenn wir Container rückwärts verarbeiten möchten , benötigen wir eine andere Methode (oder ein Methodenpaar) pro vorhandenem Algorithmus.
Der bereichsbezogene Ansatz ist also im einfachen Sinne allgemeiner:
Es gibt natürlich noch einen anderen triftigen Grund: Es war bereits eine Menge Arbeit, die STL zu standardisieren, und das Aufblasen mit Convenience-Wrappern, bevor sie weit verbreitet war, würde die begrenzte Ausschusszeit nicht besonders belasten. Bei Interesse finden Sie hier den technischen Bericht von Stepanov & Lee
Wie in den Kommentaren erwähnt, bietet Boost.Range einen neueren Ansatz, ohne dass Änderungen am Standard erforderlich sind.
quelle
f(c.begin(), c.end(), ...)
und vielleicht nur auf die am häufigsten verwendete Überlastung (wie auch immer Sie das bestimmen), um zu verhindern, dass sich die Anzahl der Überlastungen verdoppelt. Außerdem sind Iteratoradapter vollständig orthogonal (wie Sie bemerken, funktionieren sie in Python einwandfrei, dessen Iteratoren sehr unterschiedlich funktionieren und nicht über die meiste Leistung verfügen, über die Sie sprechen).std::sort(std::range(start, stop))
.#define MAKE_RANGE(container) (container).begin(), (container).end()
</ jk>Es stellte sich heraus, dass es einen Artikel von Herb Sutter zu diesem Thema gibt. Grundsätzlich liegt das Problem in der Mehrdeutigkeit der Überladung. Angesichts der folgenden:
Und das Folgende hinzufügen:
Wird es schwer zu unterscheiden
4
und1
richtig machen.Konzepte, wie sie vorgeschlagen, aber letztendlich nicht in C ++ 0x enthalten sind, hätten das gelöst, und es ist auch möglich, sie mit zu umgehen
enable_if
. Für einige Algorithmen ist dies überhaupt kein Problem. Aber sie haben sich dagegen entschieden.Nachdem ich nun alle Kommentare und Antworten hier gelesen habe, denke ich, dass
range
Objekte die beste Lösung sind. Ich denke, ich werde einen Blick darauf werfenBoost.Range
.quelle
typename Iter
scheint für eine strenge Sprache zu entenartig zu sein. Ich würde zBtemplate<typename Container> void sort(typename Container::iterator, typename Container::iterator); // 1
undtemplate<template<class> Container, typename T> void sort( Container<T>&, std::function<bool(const T&)> ); // 4
etc. bevorzugen (was vielleicht das Mehrdeutigkeitsproblem lösen würde)T[]::iterator
verfügbar sind. Außerdem muss der richtige Iterator kein verschachtelter Typ einer Sammlung sein, sondern muss nur definiert werdenstd::iterator_traits
.Grundsätzlich eine Altentscheidung. Das Iteratorkonzept basiert auf Zeigern, Container werden jedoch nicht auf Arrays modelliert. Da Arrays außerdem schwer zu übergeben sind (für die Länge wird im Allgemeinen ein nicht typisierter Vorlagenparameter benötigt), sind für eine Funktion häufig nur Zeiger verfügbar.
Aber im Nachhinein ist die Entscheidung falsch. Wir wären mit einem Range-Objekt besser dran gewesen, das entweder aus
begin/end
oder konstruiert werden kannbegin/length
. jetzt haben wir_n
stattdessen mehrere Algorithmen mit Suffix.quelle
Wenn Sie sie hinzufügen, erhalten Sie keinen Strom (Sie können bereits den gesamten Container durch Aufrufen
.begin()
und sich.end()
selbst ausführen), und der Bibliothek, die ordnungsgemäß spezifiziert, von den Anbietern zu den Bibliotheken hinzugefügt, getestet, gewartet, usw. usw.Kurz gesagt, es ist wahrscheinlich nicht vorhanden, da es sich nicht lohnt, einen Satz zusätzlicher Vorlagen zu verwalten, um Benutzern mit ganzen Containern die Eingabe eines zusätzlichen Funktionsaufrufparameters zu ersparen.
quelle
std::getline
und immer noch ist es in der Bibliothek. Man könnte sogar sagen, dass erweiterte Kontrollstrukturen mir keine Macht verschaffen, da ich alles nur mitif
und machen kanngoto
. Ja, unfairer Vergleich, ich weiß;) Ich denke, ich kann die Lasten der Spezifikation / Implementierung / Wartung irgendwie verstehen, aber es ist nur eine winzige Hülle, über die wir hier sprechen, also ..Mittlerweile ist http://en.wikipedia.org/wiki/C++11#Range-based_for_loop eine nette Alternative zu
std::for_each
. Beachten Sie, keine expliziten Iteratoren:(Inspiriert von https://stackoverflow.com/a/694534/2097284 .)
quelle
<algorithm>
gelöst, nicht für alle tatsächlich benötigten Algenbegin
undend
Iteratoren - aber der Vorteil kann nicht überbewertet werden! Als ich 2009 zum ersten Mal C ++ 03 ausprobierte, scheute ich mich vor Iteratoren zurück, weil es sich um Schleifen handelte, und glücklicherweise oder nicht, erlaubten meine damaligen Projekte dies. Der Neustart auf C ++ 11 im Jahr 2014 war ein unglaubliches Upgrade, die Sprache C ++ sollte es immer gewesen sein, und jetzt kann ich nicht mehr ohneauto &it: them
:)