Algorithmuslösung:
std::generate(numbers.begin(), numbers.end(), rand);
Bereichsbasierte For-Loop-Lösung:
for (int& x : numbers) x = rand();
Warum sollte ich std::generate
in C ++ 11 die ausführlicheren for-range-basierten for-Schleifen verwenden wollen?
begin()
undend()
?range
Funktion in ihrer Toolbox haben. (dhfor(auto& x : range(first, last))
)boost::generate(numbers, rand); // ♪
Antworten:
Die erste Version
teilt uns mit, dass Sie eine Folge von Werten generieren möchten.
In der zweiten Version muss der Leser das selbst herausfinden.
Das Speichern beim Tippen ist normalerweise nicht optimal, da es meistens in der Lesezeit verloren geht. Der meiste Code wird viel mehr gelesen als eingegeben.
quelle
numbers
ohne Grund zweimal angeben müssen . Daher :boost::range::generate(numbers, rand);
. Es gibt keinen Grund, warum Sie in einer gut aufgebauten Bibliothek nicht sowohl kürzeren als auch besser lesbaren Code haben können.std::generate(number.begin(), numbers.begin()+3, rand)
, nicht wahr? Dahernumber
kann es manchmal nützlich sein , zweimal anzugeben .std::generate()
, können Sie stattdessenstd::generate(slice(number.begin(), 3), rand)
eine hypothetische Range-Slicing-Syntax verwendenstd::generate(number[0:3], rand)
, die die Wiederholung von entfernt undnumber
gleichzeitig eine flexible Spezifikation eines Teils des Bereichs ermöglicht. Das Umgekehrte ausgehend von drei Argumentenstd::generate()
ist mühsamer.Ob die for-Schleife bereichsbasiert ist oder nicht, macht überhaupt keinen Unterschied, sie vereinfacht nur den Code in der Klammer. Algorithmen sind klarer, da sie die Absicht zeigen .
quelle
Persönlich meine erste Lektüre von:
ist "Wir weisen alles in einem Bereich zu. Der Bereich ist
numbers
weisen . Die zugewiesenen Werte sind zufällig".Meine erste Lektüre von:
ist "wir machen etwas mit allem in einem Bereich. Der Bereich ist
numbers
. Wir weisen einen zufälligen Wert zu."Die sind verdammt ähnlich, aber nicht identisch. Ein plausibler Grund, warum ich die erste Lesung provozieren möchte, ist, dass ich denke, dass die wichtigste Tatsache an diesem Code ist, dass er dem Bereich zugeordnet ist. Also gibt es dein "Warum sollte ich ...". Ich benutze,
generate
weil in C ++std::generate
"Bereichszuweisung" bedeutet. Wie übrigensstd::copy
ist der Unterschied zwischen den beiden, was Sie zuweisen.Es gibt jedoch verwirrende Faktoren. Bereichsbasierte for-Schleifen haben eine von Natur aus direktere Möglichkeit, den Bereich auszudrücken
numbers
, als iteratorbasierte Algorithmen. Deshalb arbeiten die Leute an bereichsbasierten Algorithmusbibliotheken:boost::range::generate(numbers, rand);
Sieht besser aus als diestd::generate
Version.Im Gegensatz dazu ist
int&
in Ihrer bereichsbasierten for-Schleife eine Falte. Was ist, wenn der Werttyp des Bereichs nicht der Fall ist?int
Dann machen wir hier etwas ärgerlich Feines, das davon abhängt, ob er konvertierbar istint&
, während dergenerate
Code nur von der Rückkehr abhängt,rand
wenn er dem Element zugewiesen werden kann. Selbst wenn der Werttyp istint
, könnte ich immer noch darüber nachdenken, ob dies der Fall ist oder nicht. Daherauto
wird das Nachdenken über die Typen verschoben, bis ich sehe, was zugewiesen wird - mit derauto &x
Aussage "Nehmen Sie einen Verweis auf das Bereichselement, welchen Typ auch immer". Zurück in C ++ 03 waren Algorithmen (weil sie Funktionsvorlagen sind) der Weg, um exakte Typen zu verbergen, jetzt sind sie ein Weg.Ich denke, es war schon immer so, dass die einfachsten Algorithmen nur einen geringfügigen Vorteil gegenüber den äquivalenten Schleifen haben. Range-based for-Schleifen verbessern Schleifen (hauptsächlich durch Entfernen des größten Teils der Boilerplate, obwohl sie etwas mehr enthalten). Die Ränder werden also enger und vielleicht ändern Sie Ihre Meinung in bestimmten Fällen. Aber da gibt es immer noch einen Stilunterschied.
quelle
operator int&()
? :)int&
durchSomeClass&
und jetzt müssen Sie sich um Konvertierungsoperatoren und Einzelparameter-Konstruktoren kümmern, die nicht markiert sindexplicit
.operator int&()
undoperator int const &() const
, aber andererseits könnte es durch Überladen funktionierenoperator int() const
undoperator=(int)
.Meiner Meinung nach Effective STL Item 43: "Bevorzugen Sie Algorithmusaufrufe gegenüber handgeschriebenen Schleifen." ist immer noch ein guter Rat.
Normalerweise schreibe ich Wrapper-Funktionen, um die
begin()
/end()
Hölle loszuwerden . Wenn Sie das tun, sieht Ihr Beispiel folgendermaßen aus:Ich glaube, es übertrifft den auf Schleifen basierenden Bereich sowohl bei der Kommunikation der Absicht als auch bei der Lesbarkeit.
Trotzdem muss ich zugeben, dass in C ++ 98 einige STL-Algorithmusaufrufe unaussprechlichen Code ergaben und das Befolgen von "Algorithmusaufrufe gegenüber handgeschriebenen Schleifen bevorzugen" keine gute Idee zu sein schien. Zum Glück haben Lambdas das geändert.
Betrachten Sie das folgende Beispiel von Herb Sutter: Lambdas, Lambdas Everywhere .
Aufgabe: Finden Sie das erste Element in v, das
> x
und ist< y
.Ohne Lambdas:
Mit Lambda
quelle
In meiner Meinung nach ist die manuelle Schleife, obwohl Ausführlichkeit reduzieren könnte, fehlt readabitly:
Ich würde nicht diese Schleife verwenden zu initialisieren 1 durch definierte der Bereich Zahlen , denn wenn ich es sehe, scheint es mir , dass es Iterieren über eine Reihe von Zahlen, aber es funktioniert nicht (im Wesentlichen) in der Wirklichkeit, das heißt statt Lesen aus dem Bereich, es schreibt auf den Bereich.
Die Absicht ist viel klarer, wenn Sie verwenden
std::generate
.1. Initialisieren bedeutet in diesem Zusammenhang , den Elementen des Containers einen sinnvollen Wert zu geben .
quelle
std::generate
, was von einem C ++ - Programmierer angenommen werden kann (wenn sie nicht vertraut sind, werden sie es nachschlagen, dasselbe Ergebnis).&
Zeichen vergessen ?). Der Vorteil von Algorithmen besteht darin, dass sie Absichten zeigen, während Sie bei Schleifen darauf schließen müssen. Wenn bei der Implementierung der Schleife ein Fehler vorliegt, ist nicht klar, ob es sich um einen Fehler handelt oder ob dies beabsichtigt war.std::generate
eines bloßen Blicks kann man sagen , dass durch diese Funktion etwas generiert wird. Was das ist, wird durch das dritte Argument auf die Funktion beantwortet. Ich denke das ist viel besser.Es gibt einige Dinge, die Sie (einfach) nicht mit bereichsbasierten Schleifen tun können, die Algorithmen, die Iteratoren als Eingabe verwenden, können. Zum Beispiel mit
std::generate
:Füllen Sie den Container bis
limit
(ausgeschlossen,limit
ist ein gültiger Iterator aktiviertnumbers
) mit Variablen aus einer Distribution und den Rest mit Variablen aus einer anderen Distribution.Mit Iterator-basierten Algorithmen können Sie den Bereich, in dem Sie arbeiten, besser steuern.
quelle
Für den speziellen Fall von
std::generate
stimme ich den vorherigen Antworten zum Thema Lesbarkeit / Absicht zu. std :: generate scheint mir eine klarere Version zu sein. Aber ich gebe zu, dass dies in gewisser Weise Geschmackssache ist.Trotzdem habe ich einen weiteren Grund, den std :: -Algorithmus nicht wegzuwerfen - es gibt bestimmte Algorithmen, die auf einige Datentypen spezialisiert sind.
Das einfachste Beispiel wäre
std::fill
. Die allgemeine Version wird als for-Schleife über den bereitgestellten Bereich implementiert. Diese Version wird beim Instanziieren der Vorlage verwendet. Aber nicht immer. ZB wenn Sie ihm einen Bereich zur Verfügung stellen, der a iststd::vector<int>
- oft wird er tatsächlich anrufenmemset
unter der Haube , was einen viel schnelleren und besseren Code ergibt.Also versuche ich hier eine Effizienzkarte zu spielen.
Ihre handgeschriebene Schleife ist möglicherweise so schnell wie eine std :: algorithm-Version, kann jedoch kaum schneller sein. Darüber hinaus ist der std :: -Algorithmus möglicherweise auf bestimmte Container und Typen spezialisiert und erfolgt über die saubere STL-Schnittstelle.
quelle
Meine Antwort wäre vielleicht und nein. Wenn wir über C ++ 11 sprechen, dann vielleicht (eher wie nein). Zum Beispiel
std::for_each
ist es wirklich ärgerlich, es auch mit Lambdas zu benutzen:Die Verwendung von Range-Based für ist jedoch viel besser:
Auf der anderen Seite, wenn wir über C ++ 1y sprechen, würde ich argumentieren, dass nein, die Algorithmen nicht durch bereichsbasierte für überholt werden. Im C ++ - Standardkomitee gibt es eine Studiengruppe, die an einem Vorschlag arbeitet, Bereiche zu C ++ hinzuzufügen, und es wird auch an polymorphen Lambdas gearbeitet. Bereiche würden die Notwendigkeit der Verwendung von Iteratorpaaren beseitigen, und polymorphes Lambda würde es Ihnen ermöglichen, den genauen Argumenttyp des Lambda nicht anzugeben. Dies bedeutet, dass
std::for_each
dies so verwendet werden könnte (nehmen Sie dies nicht als harte Tatsache, es ist genau so, wie die Träume heute aussehen):quelle
[]
mit dem Lambda die Nullerfassung angeben. Das heißt, im Vergleich zum Schreiben eines Schleifenkörpers haben Sie einen Teil des Codes aus dem Kontext der variablen Suche isoliert, in dem er lexikalisch angezeigt wird. Die Isolierung ist für den Leser normalerweise hilfreich, weniger zum Nachdenken beim Lesen.for_each
auch bei Verwendung mit einem Lambda noch sinnlos ist. foreach + Capture Lambda ist derzeit eine ausführliche Methode zum Schreiben einer bereichsbasierten for-Schleife. Sie wird etwas weniger ausführlich, aber immer noch mehr als die Schleife.for_each
Natürlich denke ich nicht, dass Sie sich verteidigen müssen, aber noch bevor ich Ihre Antwort sah, dachte ich, wenn der Fragesteller Algorithmen verprügeln wollte, hätte erfor_each
das weichste aller möglichen Ziele auswählen können ;-)for_each
, aber es hat einen winzigen Vorteil gegenüber bereichsbasiert für - Sie können es einfacher parallel machen, indem Sie ihm parallel_ voranstellen, um es zu verwandelnparallel_for_each
(wenn Sie PPL verwenden und davon ausgehen, dass es threadsicher ist). . :-Dstd::algorithm
Implementierung des s hinter seiner Schnittstelle verborgen ist und beliebig komplex (oder beliebig optimiert) sein kann.Eine Sache, die beachtet werden sollte, ist, dass ein Algorithmus ausdrückt, was getan wird, nicht wie.
Die bereichsbasierte Schleife umfasst die Art und Weise, wie Dinge erledigt werden: Beginnen Sie mit dem ersten, wenden Sie an und gehen Sie bis zum Ende zum nächsten Element. Sogar ein einfacher Algorithmus könnte die Dinge anders machen (zumindest einige Überladungen für bestimmte Container, ohne an den schrecklichen Vektor zu denken), und zumindest ist die Art und Weise, wie dies gemacht wird, nicht das Geschäft der Autoren.
Für mich ist das ein großer Unterschied, kapseln Sie so viel wie möglich ein, und das rechtfertigt den Satz, wenn Sie können, verwenden Sie Algorithmen.
quelle
Eine bereichsbasierte for-Schleife ist genau das. Bis natürlich Standard geändert wird.
Algorithmus ist eine Funktion. Eine Funktion, die einige Anforderungen an ihre Parameter stellt. Die Anforderungen sind in einem Standard formuliert, um beispielsweise eine Implementierung zu ermöglichen, die alle verfügbaren Ausführungsthreads nutzt und Sie automatisch beschleunigt.
quelle