Gibt es irgendwelche Vorteile von std::for_each
Over- for
Loop? Mir std::for_each
scheint nur die Lesbarkeit von Code zu beeinträchtigen. Warum empfehlen dann einige Codierungsstandards die Verwendung?
c++
stl
foreach
coding-style
fehlender Faktor
quelle
quelle
std::for_each
bei Verwendung mitboost.lambda
oderboost.bind
kann oft die Lesbarkeit verbessernAntworten:
Das Schöne an C ++ 11 (früher C ++ 0x genannt) ist, dass diese lästige Debatte beigelegt wird.
Ich meine, niemand, der bei klarem Verstand ist und eine ganze Sammlung durchlaufen möchte, wird dies noch verwenden
Oder dieses
wenn die bereichsbasierte
for
Schleifensyntax verfügbar ist:Diese Art von Syntax ist seit einiger Zeit in Java und C # verfügbar, und tatsächlich gibt es in jedem neueren Java- oder C # -Code, den ich gesehen habe, weit mehr
foreach
Schleifen als klassischefor
Schleifen.quelle
Element & e
wieauto & e
(oderauto const &e
) besser aussieht. Ich würdeElement const e
(ohne Referenz) verwenden, wenn ich implizite Konvertierung möchte, z. B. wenn die Quelle eine Sammlung verschiedener Typen ist und ich möchte, dass sie in konvertiert werdenElement
.Hier sind einige Gründe:
Es scheint die Lesbarkeit zu beeinträchtigen, nur weil Sie nicht daran gewöhnt sind und / oder nicht die richtigen Tools verwenden, um es wirklich einfach zu machen. (Weitere Informationen finden Sie unter boost :: range und boost :: bind / boost :: lambda. Viele davon werden in C ++ 0x verwendet und for_each und verwandte Funktionen sind nützlicher.)
Sie können einen Algorithmus über for_each schreiben, der mit jedem Iterator funktioniert.
Es verringert die Wahrscheinlichkeit von dummen Tippfehlern.
Es öffnet auch Ihre Meinung zu dem Rest der STL-Algorithmen, wie
find_if
,sort
,replace
usw. , und diese werden nicht so seltsam aussehen mehr. Dies kann ein großer Gewinn sein.Update 1:
Am wichtigsten ist, dass es Ihnen hilft, über
for_each
For-for-Schleifen hinauszugehen, wie es alles ist, und sich die anderen STL-Alogs anzusehen, wie find / sort / partition / copy_replace_if, Parallell-Ausführung ... oder was auch immer.Mit "dem Rest" der Geschwister von for_each kann eine Menge Verarbeitung sehr präzise geschrieben werden. Wenn Sie jedoch nur eine for-Schleife mit verschiedenen internen Logiken schreiben, werden Sie nie lernen, wie man diese verwendet, und Sie werden es tun am Ende immer wieder das Rad erfinden.
Und (der bald verfügbare Range-Style für_each):
Oder mit C ++ x11 Lambdas:
ist IMO besser lesbar als:
Auch dies (oder mit Lambdas, siehe andere):
Ist prägnanter als:
Vor allem, wenn Sie mehrere Funktionen in der richtigen Reihenfolge aufrufen müssen ... aber vielleicht bin das nur ich. ;)
Update 2 : Ich habe meine eigenen Einzeiler-Wrapper von STL-Algen geschrieben, die mit Bereichen anstelle von Iteratorpaaren arbeiten. boost :: range_ex wird das nach seiner Veröffentlichung enthalten und vielleicht auch in C ++ 0x?
quelle
outer_class::inner_class::iterator
oder sie sind Vorlagenargumente:typename std::vector<T>::iterator
... das for-Konstrukt selbst kann auf ein Konstrukt mit vielen Zeilen an sichfor_each
im zweiten Beispiel ist falsch (sollte seinfor_each( bananas.begin(), bananas.end(),...
for_each
ist allgemeiner. Sie können es verwenden, um über jeden Containertyp zu iterieren (indem Sie die Start- / End-Iteratoren übergeben). Sie können möglicherweise Container unter einer Funktion austauschen, die verwendet wird,for_each
ohne den Iterationscode aktualisieren zu müssen. Sie müssen berücksichtigen, dass es auf der Welt andere Container alsstd::vector
einfache C-Arrays gibt, um die Vorteile von zu erkennenfor_each
.Der Hauptnachteil von
for_each
ist, dass es einen Funktor braucht, so dass die Syntax klobig ist. Dies wird in C ++ 11 (früher C ++ 0x) mit der Einführung von Lambdas behoben:Dies wird in 3 Jahren nicht sonderbar für Sie aussehen.
quelle
for ( int v : int_vector ) {
(auch wenn es heute mit BOOST_FOREACH simuliert werden kann)std::for_each(container, [](int& i){ ... });
. Ich meine, warum muss man zweimal Container schreiben?container.each { ... }
ohne Erwähnung von Anfangs- und Enditeratoren. Ich finde es etwas überflüssig, dass ich ständig den Enditerator angeben muss.Persönlich finde ich jedes Mal, wenn ich mir die Mühe machen müsste
std::for_each
, spezielle Funktionen zu schreiben (spezielle Funktoren / komplizierteboost::lambda
s schreiben ),BOOST_FOREACH
und C ++ 0x ist bereichsbasiert, um klarer zu werden:vs.
quelle
sein sehr subjektiv, werden einige sagen , dass die Verwendung
for_each
des Code macht mehr lesbar, da es verschiedene Sammlungen mit den gleichen Konventionen zu behandeln ermöglicht.for_each
itslef ist als Schleife implementiertEs liegt also an Ihnen, zu entscheiden, was für Sie richtig ist.
quelle
Wie bei vielen Algorithmusfunktionen besteht eine erste Reaktion darin, zu denken, dass es unlesbarer ist, foreach als eine Schleife zu verwenden. Es war ein Thema vieler Flammenkriege.
Sobald Sie sich an die Redewendung gewöhnt haben, finden Sie sie möglicherweise nützlich. Ein offensichtlicher Vorteil besteht darin, dass der Codierer gezwungen wird, den inneren Inhalt der Schleife von der eigentlichen Iterationsfunktionalität zu trennen. (OK, ich denke, es ist ein Vorteil. Andere sagen, Sie zerhacken nur den Code ohne wirklichen Nutzen).
Ein weiterer Vorteil ist, dass ich weiß, dass entweder jeder Artikel verarbeitet oder eine Ausnahme ausgelöst wird, wenn ich foreach sehe .
Eine for- Schleife bietet verschiedene Möglichkeiten zum Beenden der Schleife. Sie können die Schleife ihren vollen Verlauf nehmen lassen oder das Schlüsselwort break verwenden, um explizit aus der Schleife zu springen, oder das Schlüsselwort return verwenden , um die gesamte Funktion in der Mitte der Schleife zu beenden. Im Gegensatz dazu lässt foreach diese Optionen nicht zu und ist daher besser lesbar. Sie können nur einen Blick auf den Funktionsnamen werfen und kennen die vollständige Art der Iteration.
Hier ist ein Beispiel für eine verwirrende for- Schleife:
quelle
std::for_each()
den alten Standard (den der Zeit dieses Beitrags) verwenden, müssen Sie einen benannten Funktor verwenden, der die Lesbarkeit fördert, wie Sie sagen, und ein vorzeitiges Ausbrechen der Schleife verhindert. Aber dann hat die äquivalentefor
Schleife nichts als einen Funktionsaufruf, und auch das verhindert ein vorzeitiges Ausbrechen. Aber abgesehen davon denke ich, dass Sie einen hervorragenden Punkt gemacht haben, indem Sie gesagt haben, dass das Durchlaufen des gesamten Sortimentsstd::for_each()
erzwungen wird.Sie haben größtenteils Recht: Meistens
std::for_each
handelt es sich um einen Nettoverlust. Ich würde sogar so weit gehen zu vergleichenfor_each
zugoto
.goto
bietet die vielseitigste Flusssteuerung, die möglich ist - Sie können damit praktisch jede andere Steuerungsstruktur implementieren, die Sie sich vorstellen können. Diese Vielseitigkeit bedeutet jedoch, dass dasgoto
isolierte Betrachten praktisch nichts darüber aussagt, was in dieser Situation beabsichtigt ist. Infolgedessen nutzt fast niemand, der bei klarem Verstand ist,goto
außer als letztes Mittel.Bei den Standardalgorithmen
for_each
ist es ähnlich - es kann verwendet werden, um praktisch alles zu implementieren, was bedeutet, dass das Sehenfor_each
Ihnen praktisch nichts darüber sagt, wofür es in dieser Situation verwendet wird. Leider geht es bei der Einstellung der Menschenfor_each
darum, wo ihre Einstellung zugoto
(etwa) 1970 war - einige Leute hatten die Tatsache begriffen, dass es nur als letztes Mittel verwendet werden sollte, aber viele halten es immer noch für den primären Algorithmus, und Verwenden Sie selten oder nie einen anderen. Die überwiegende Mehrheit der Zeit würde sogar ein kurzer Blick zeigen, dass eine der Alternativen drastisch überlegen war.Ich bin mir zum Beispiel ziemlich sicher, dass ich den Überblick verloren habe, wie oft ich Leute gesehen habe, die Code geschrieben haben, um den Inhalt einer Sammlung mit auszudrucken
for_each
. Basierend auf Beiträgen, die ich gesehen habe, ist dies möglicherweise die häufigste Verwendungfor_each
. Sie enden mit so etwas wie:Und ihr Beitrag fragt nach , welche Kombination von
bind1st
,mem_fun
usw. müssen sie so etwas wie zu machen:arbeiten und drucken Sie die Elemente von
coll
. Wenn es wirklich genau so funktioniert hätte, wie ich es dort geschrieben habe, wäre es mittelmäßig, aber es funktioniert nicht - und wenn Sie es zum Laufen gebracht haben, ist es schwierig, diese wenigen Code-Teile zu finden, die sich auf das beziehen, was mit dem zu tun hat weiter zwischen den Stücken, die es zusammenhalten.Zum Glück gibt es einen viel besseren Weg. Fügen Sie eine normale Stream Inserter-Überladung für XXX hinzu:
und verwenden
std::copy
:Das funktioniert - und es ist praktisch keine Arbeit erforderlich, um herauszufinden, dass der Inhalt von
coll
to gedruckt wirdstd::cout
.quelle
boost::mem_fn(&XXX::print)
eher sein alsXXX::print
std::cout
als Argument binden, damit es funktioniert).Der Vorteil der Schreibfunktion, um besser lesbar zu sein, zeigt sich möglicherweise nicht, wann
for(...)
undfor_each(...
).Wenn Sie alle Algorithmen in function.h verwenden, anstatt for-Schleifen zu verwenden, wird der Code viel besser lesbar.
ist viel besser lesbar als;
Und das finde ich so schön, verallgemeinere die for-Schleifen auf einzeilige Funktionen =)
quelle
Einfach:
for_each
Ist nützlich, wenn Sie bereits über eine Funktion verfügen, mit der jedes Array-Element verarbeitet werden kann, sodass Sie kein Lambda schreiben müssen. Sicher dasist besser als
Außerdem durchläuft die
for
Fernschleife nur ganze Container von Anfang bis Ende, währendfor_each
sie flexibler ist.quelle
Die
for_each
Schleife soll die Iteratoren (Details zur Implementierung einer Schleife) vor dem Benutzercode verbergen und eine klare Semantik für die Operation definieren: Jedes Element wird genau einmal iteriert.Das Problem mit der Lesbarkeit im aktuellen Standard besteht darin, dass anstelle eines Codeblocks ein Funktor als letztes Argument erforderlich ist. In vielen Fällen müssen Sie daher einen bestimmten Funktortyp dafür schreiben. Dies führt zu weniger lesbarem Code, da Funktorobjekte nicht direkt definiert werden können (lokale Klassen, die innerhalb einer Funktion definiert sind, können nicht als Vorlagenargumente verwendet werden) und die Implementierung der Schleife von der eigentlichen Schleife entfernt werden muss.
Beachten Sie, dass Sie, wenn Sie für jedes Objekt eine bestimmte Operation ausführen möchten
std::mem_fn
, oderboost::bind
(std::bind
im nächsten Standard) oderboost::lambda
(Lambdas im nächsten Standard) verwenden können, um dies zu vereinfachen:Dies ist nicht weniger lesbar und kompakter als die handgerollte Version, wenn Sie über eine Funktion / Methode zum Aufrufen verfügen. Die Implementierung könnte andere Implementierungen der
for_each
Schleife bereitstellen (denken Sie an Parallelverarbeitung).Der kommende Standard behebt einige der Mängel auf unterschiedliche Weise und ermöglicht lokal definierte Klassen als Argumente für Vorlagen:
Verbessern der Lokalität von Code: Wenn Sie surfen, sehen Sie, was er genau dort tut. Tatsächlich müssen Sie nicht einmal die Klassensyntax verwenden, um den Funktor zu definieren, sondern verwenden genau dort ein Lambda:
Auch wenn es für den Fall von
for_each
ein spezifisches Konstrukt gibt, das es natürlicher macht:Ich neige dazu, das
for_each
Konstrukt mit handgerollten Schleifen zu mischen . Wenn ich nur einen Aufruf einer vorhandenen Funktion oder Methode benötige (for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
), greife ich zu demfor_each
Konstrukt, das dem Code eine Menge Kesselplatten-Iterator-Material wegnimmt. Wenn ich etwas Komplexeres brauche und einen Funktor nicht nur ein paar Zeilen über dem tatsächlichen Gebrauch implementieren kann, rolle ich meine eigene Schleife (hält die Operation an Ort und Stelle). In unkritischen Codeabschnitten könnte ich mich für BOOST_FOREACH entscheiden (ein Mitarbeiter hat mich dazu gebracht).quelle
Neben der Lesbarkeit und Leistung wird häufig die Konsistenz übersehen. Es gibt viele Möglichkeiten, eine for (oder while) -Schleife über Iteratoren zu implementieren:
zu:
mit vielen Beispielen dazwischen bei unterschiedlichen Effizienz- und Fehlerpotentialen.
Die Verwendung von for_each erzwingt jedoch die Konsistenz, indem die Schleife entfernt wird:
Das einzige, worüber Sie sich jetzt Sorgen machen müssen, ist: Implementieren Sie den Schleifenkörper als Funktion, Funktor oder Lambda mithilfe von Boost- oder C ++ 0x-Funktionen? Persönlich würde ich mir lieber darum kümmern, als eine zufällige for / while-Schleife zu implementieren oder zu lesen.
quelle
Ich mochte es nicht
std::for_each
und dachte, dass es ohne Lambda völlig falsch gemacht wurde. Allerdings habe ich es mir vor einiger Zeit anders überlegt und jetzt liebe ich es wirklich. Ich denke, es verbessert sogar die Lesbarkeit und erleichtert das Testen Ihres Codes auf TDD-Weise.Der
std::for_each
Algorithmus kann mit allen Elementen im Bereich gelesen werden , was die Lesbarkeit verbessern kann . Angenommen, die Aktion, die Sie ausführen möchten, ist 20 Zeilen lang, und die Funktion, in der die Aktion ausgeführt wird, ist ebenfalls etwa 20 Zeilen lang. Das würde eine Funktion mit einer herkömmlichen for-Schleife 40 Zeilen lang und mit einer nur etwa 20 Zeilen lang machenstd::for_each
, was wahrscheinlich einfacher zu verstehen ist.Funktoren für
std::for_each
sind eher generisch und daher wiederverwendbar, z.Und im Code hätten Sie nur einen Einzeiler,
std::for_each(v.begin(), v.end(), DeleteElement())
der IMO etwas besser ist als eine explizite Schleife.Alle diese Funktoren sind normalerweise leichter unter Unit-Tests zu finden als eine explizite for-Schleife mitten in einer langen Funktion, und das allein ist für mich bereits ein großer Gewinn.
std::for_each
ist im Allgemeinen auch zuverlässiger, da Sie weniger wahrscheinlich einen Fehler mit der Reichweite machen.Und schließlich kann der Compiler etwas besseren Code für
std::for_each
bestimmte handgefertigte for-Schleifen erzeugen als für bestimmte Arten von handgefertigten for-Schleifen, da er (for_each) für Compiler immer gleich aussieht und Compiler-Autoren ihr gesamtes Wissen einsetzen können, um ihn so gut wie möglich zu machen können.Gleiches gilt für andere std Algorithmen wie
find_if
,transform
usw.quelle
for
ist für eine Schleife, die jedes Element oder jedes Drittel usw. iterieren kann.for_each
dient zum Iterieren nur jedes Elements. Es ist klar aus seinem Namen. Es ist also klarer, was Sie in Ihrem Code vorhaben.quelle
++
. Vielleicht ungewöhnlich, aber auch eine For-Schleife macht das Gleiche.transform
jemanden nicht zu verwirren.Wenn Sie häufig andere Algorithmen aus der STL verwenden, gibt es mehrere Vorteile
for_each
:Im Gegensatz zu einer herkömmlichen for-Schleife müssen
for_each
Sie Code schreiben, der für jeden Eingabe-Iterator funktioniert. Auf diese Weise eingeschränkt zu sein, kann tatsächlich eine gute Sache sein, weil:for_each
.Die Verwendung
for_each
macht manchmal deutlicher, dass Sie eine spezifischere STL-Funktion verwenden können, um dasselbe zu tun. (Wie in Jerry Coffins Beispiel; es ist nicht unbedingt der Fall, derfor_each
die beste Option ist, aber eine for-Schleife ist nicht die einzige Alternative.)quelle
Mit C ++ 11 und zwei einfachen Vorlagen können Sie schreiben
als Ersatz für
for_each
oder als Schleife. Warum es auf Kürze und Sicherheit ankommt, es besteht keine Möglichkeit eines Fehlers in einem Ausdruck, der nicht vorhanden ist.Für mich
for_each
war es aus den gleichen Gründen immer besser, wenn der Loop-Body bereits ein Funktor ist, und ich werde jeden Vorteil nutzen, den ich bekommen kann.Sie verwenden immer noch den Drei-Ausdruck
for
, aber wenn Sie jetzt einen sehen, wissen Sie, dass es dort etwas zu verstehen gibt, es ist kein Boilerplate. Ich hasse Boilerplate. Ich ärgere mich über seine Existenz. Es ist kein echter Code, es gibt nichts zu lernen, wenn man ihn liest. Es ist nur eine weitere Sache, die überprüft werden muss. Die mentale Anstrengung kann daran gemessen werden, wie leicht es ist, bei der Überprüfung rostig zu werden.Die Vorlagen sind
quelle
Meistens müssen Sie die gesamte Sammlung durchlaufen . Daher schlage ich vor, dass Sie Ihre eigene for_each () -Variante schreiben und nur 2 Parameter verwenden. Auf diese Weise können Sie das Beispiel von Terry Mahaffey wie folgt umschreiben :
Ich denke, das ist in der Tat besser lesbar als eine for-Schleife. Dies erfordert jedoch die C ++ 0x-Compiler-Erweiterungen.
quelle
Ich finde for_each schlecht für die Lesbarkeit. Das Konzept ist gut, aber C ++ macht es sehr schwer, lesbar zu schreiben, zumindest für mich. c ++ 0x Lamda-Ausdrücke helfen. Ich mag die Idee von Lamdas sehr. Auf den ersten Blick finde ich die Syntax jedoch sehr hässlich und ich bin mir nicht 100% sicher, ob ich mich jemals daran gewöhnen werde. Vielleicht habe ich mich in 5 Jahren daran gewöhnt und nicht weiter darüber nachgedacht, aber vielleicht auch nicht. Wir werden sehen :)
Ich benutze lieber
Ich finde eine explizite for-Schleife klarer zu lesen und die Explizität unter Verwendung benannter Variablen für die Start- und End-Iteratoren reduziert die Unordnung in der for-Schleife.
Natürlich variieren die Fälle, das ist genau das, was ich normalerweise am besten finde.
quelle
Der Iterator kann ein Aufruf einer Funktion sein, die bei jeder Iteration durch die Schleife ausgeführt wird.
Siehe hier: http://www.cplusplus.com/reference/algorithm/for_each/
quelle
for_each
in diesem Fall die Frage nach den Vorteilen nicht beantwortet.For-Schleife kann brechen; Ich möchte kein Papagei für Herb Sutter sein, daher hier der Link zu seiner Präsentation: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Lesen Sie auch die Kommentare :)
quelle
for_each
Erlauben Sie uns, das Fork-Join-Muster zu implementieren . Ansonsten unterstützt es Fluent-Interface .Gabelverbindungsmuster
Wir können eine Implementierung hinzufügen
gpu::for_each
, um cuda / gpu für heterogen-paralleles Rechnen zu verwenden, indem wir die Lambda-Aufgabe in mehreren Workern aufrufen.Und
gpu::for_each
kann warten, bis die Arbeiter an allen Lambda-Aufgaben fertig sind, bevor sie die nächsten Anweisungen ausführen.fließende Schnittstelle
Es ermöglicht uns, von Menschen lesbaren Code präzise zu schreiben.
quelle