for Schleife vs std::for_each + lambda
-
unter Python wird das auch hochgeehrt und umjubelt.
Python? Wir sind im C++-Forum!
Nexus schrieb:
Sone schrieb:
Außerdem lässt sich alles mit Makros vereinfachen.
#define ITERATE( range, it_name ) \ for(auto it_name = (range).begin(); it_name != (range).end(); ++it_name)
ITERATE(object.getList(), itr) { itr->fn(); }
BAM
Das sehe ich als persönliche Herausforderung:
template<typename T> auto determine_type [[noreturn]] ( T&& t ) -> decltype(std::forward<T>(t)) {} #define ITERATE( range, it_name ) \ for( bool _loopcond = true; _loopcond ; ) \ for( decltype(determine_type(range)) _range = range; _loopcond; _loopcond = false ) \ for( auto it_name = std::begin(_range); it_name != std::end(_range); ++it_name )
P.S.: Ja, paranoid ist hier völlig falsch.
-
Warum
decltype(determine_type(range))
und nichtauto&&
?
-
1. WTF:
Nexus schrieb:
Warum
decltype(determine_type(range))
und nichtauto&&
?Damit er ein völlig sinnloses
[[noreturn]]
reinstopfen kann, obwohl in dem Fall ein Auslassen des Funktionsbodys, oder sogar ein auto&& gereicht hätte.2. WTF:
begin() wird nicht geadlt.namespace sone { struct mess{ mess(int i) : i(i) {} mess(mess const&) =delete; mess(mess&&) =delete; int i; }; int *begin(sone::mess& m) { return &m.i; } int *end(sone::mess& m) { return &m.i + 1; } }
3. WTF:
Geht nicht mit initializer_list#define LIST {1,2,3,4} // ja ich bin nett und versuche gar nicht, das als Parameter zu übergeben ITERATE(LIST, itr) { // forensyntaxhighlighter ftw std::cout << *itr << '\n'; break; }
-
Also Containerabstraktion/Algorithmenabstraktion laeuft ueber Iteratoren in C++. D.h. Iteratoren sind in idiomatischem C++ vorzuziehen, also auch vor
for(auto e: cont) { ... }
. Deswegen ist for_each nur konsequent. Nun, mitlerweile hat man gemerkt, dass man sowieso meist mit ganzen Containern arbeitet, zumal sich Iteratoren nur schlecht kombinieren lassen.@Sone: Lass doch einfach diese ganzen Makros. Makros are evil. Genau wie goto sollte man den Einsatz begruenden. Ich persoenlich habe kein Bock, deine Makros zu verstehen, egal wie einfach sie scheinen moegen. Bleiben wir doch bei C++.
-
Hmm, bin ich wohl der einzige, der einfacch std::for_each verwendet wenn er eine unäre Funktion auf eine Range anwenden möchte.
vector<void*> ptrs; for_each(ptrs.begin(), ptrs.end(), &free);
(Jaja, in dem Fall würde man RAII nutzen ...)
-
knivil schrieb:
Also Containerabstraktion/Algorithmenabstraktion laeuft ueber Iteratoren in C++. D.h. Iteratoren sind in idiomatischem C++ vorzuziehen, also auch vor
for(auto e: cont) { ... }
. Deswegen ist for_each nur konsequent.Range-based loops laufen doch auch über Iteratoren, sind also genauso konsequent.
Die Syntax ist zwar eine andere, aber insgesamt sind sie doch deutlich einfacher zu lesen, vor allem wenn man auch noch auto als Typ verwendet.
Wenn man hingegen schon ein Funktionsobjekt oder eine Funktion hat, sieht es deutlich übersichtlicher aus und dann ist std::for_each vorzuziehen.Edit: Insgesamt nimmt sich das aber nicht viel, also soll es doch jeder so machen, wie es ihm gefällt.
-
Nexus schrieb:
Warum
decltype(determine_type(range))
und nichtauto&&
?Hab ich gar nicht gesehen. Ich habe den Trick schon im Standard gefunden, dann aber wieder vergessen.
Das [[noreturn]] war jetzt analog zu declval, nur so als Hinweis dass diese Funktion niemals aufgerufen werden soll und keiner nach einer Definition suchen soll. (Hätte zwar auch ein Kommentar getan, aber tja).sone__mess schrieb:
2. WTF:
begin() wird nicht geadlt.Ja, dafür muss was her was §6.5.4/1 entspricht.
3. WTF:
Geht nicht mit initializer_listGeht wunderbar. Mit GCC 4.9.0.
-
Sone schrieb:
sone__mess schrieb:
2. WTF:
begin() wird nicht geadlt.Ja, dafür muss was her was §6.5.4/1 entspricht.
Eine kleine Hilfsfunktion?
-
knivil schrieb:
D.h. Iteratoren sind in idiomatischem C++ vorzuziehen, also auch vor
for(auto e: cont) { ... }
. Deswegen ist for_each nur konsequent.Ne, du musst von deinem funktionalen Denken wegkommen.
std::for_each()
über einen ganzen Container mit Lambdas aufrufen bringt meist nichts, weil man das Gleiche einfacher über Range-Based For erreichen kann.knivil schrieb:
Nun, mitlerweile hat man gemerkt, dass man sowieso meist mit ganzen Containern arbeitet, zumal sich Iteratoren nur schlecht kombinieren lassen.
Was meinst du damit?
-
Sone schrieb:
Nexus schrieb:
Warum
decltype(determine_type(range))
und nichtauto&&
?Hab ich gar nicht gesehen. Ich habe den Trick schon im Standard gefunden, dann aber wieder vergessen.
Das [[noreturn]] war jetzt analog zu declval, nur so als Hinweis dass diese Funktion niemals aufgerufen werden soll und keiner nach einer Definition suchen soll. (Hätte zwar auch ein Kommentar getan, aber tja).Wie wär's in dem Fall mit
= delete;
?[[noreturn]]
würde mir immer noch erlauben, die Funktion zu definieren und aufzurufen, was wann zu UB führt...
-
camper schrieb:
Sone schrieb:
sone__mess schrieb:
2. WTF:
begin() wird nicht geadlt.Ja, dafür muss was her was §6.5.4/1 entspricht.
Eine kleine Hilfsfunktion?
namespace RangeAccess { using std::begin; template<typename C> inline auto adlbegin( C&& c ) -> decltype( begin(std::forward<C>(c)) ) { return begin(std::forward<C>(c)); } using std::end; template<typename C> inline auto adlend( C&& c ) -> decltype( end(std::forward<C>(c)) ) { return end(std::forward<C>(c)); } }
? Oder muss für
const
und non-const
lvalue-Referenzen definiert werden?
-
Range-based loops laufen doch auch über Iteratoren, sind also genauso konsequent.
Mir ging es um das Sprachmittel, nicht darum, das dahinter auch nur Iteratoren benutzt werden. Weil dann muss diese Kette konsequent bis Asm fortgefuehrt werden.
Die Syntax ist zwar eine andere, aber insgesamt sind sie doch deutlich einfacher zu lesen, vor allem wenn man auch noch auto als Typ verwendet.
Ja stimme ich zu, habe ich erwaehnt.
-
Nexus schrieb:
knivil schrieb:
D.h. Iteratoren sind in idiomatischem C++ vorzuziehen, also auch vor
for(auto e: cont) { ... }
. Deswegen ist for_each nur konsequent.Ne, du musst von deinem funktionalen Denken wegkommen.
std::for_each()
über einen ganzen Container mit Lambdas aufrufen bringt meist nichts, weil man das Gleiche einfacher über Range-Based For erreichen kann.knivil schrieb:
Nun, mitlerweile hat man gemerkt, dass man sowieso meist mit ganzen Containern arbeitet, zumal sich Iteratoren nur schlecht kombinieren lassen.
Was meinst du damit?
1.) Nun, ich habe aus einer bestimmten Sicht argumentiert. Hier war es die ideomatische Sichtweise von pre-C++11 mit Iteratoren als Containerabstraktion fuer Algorithmen. Ich persoenlich nehme ganz klar
for(auto e: cont) { ... }
.
2.) Es ist in C++ schwierig, wie in funktionalen Sprachen, sort, accumulate, reduce etc. einfach zu kombinieren. Ein Grund dafuer ist, Iteratoren als Abstraktion zu verwenden. Wie implementiere ich beispielsweise zip und wende dann ein accumulate darauf an? Macht man einfach nicht, weils sehr umstaendlich ist, das kanonisch ueber Iteratoren als Abstraktion zu realisieren. Ranges loesen das Problem nicht.