for Schleife vs std::for_each + lambda



  • dot schrieb:

    Iteratoren sind in einem Debug Build per default checked, das kann man aber selbstverständlich deaktivieren, was im Release Build natürlich per default passiert...

    Ich bin mir 90% sicher dass die per Default immer checked sind.



  • hustbaer schrieb:

    dot schrieb:

    Iteratoren sind in einem Debug Build per default checked, das kann man aber selbstverständlich deaktivieren, was im Release Build natürlich per default passiert...

    Ich bin mir 90% sicher dass die per Default immer checked sind.

    Mit Debug mehr als mit Release, aber wenn man keinen Check will muss man das mit Defineschaltern abstellen.



  • manni66 schrieb:

    hustbaer schrieb:

    dot schrieb:

    Iteratoren sind in einem Debug Build per default checked, das kann man aber selbstverständlich deaktivieren, was im Release Build natürlich per default passiert...

    Ich bin mir 90% sicher dass die per Default immer checked sind.

    Mit Debug mehr als mit Release, aber wenn man keinen Check will muss man das mit Defineschaltern abstellen.

    Korrektur: in VS2012 sind die Schalter im Release per default aus.



  • Sone:
    Geschmackssache, was implizite Iteratorenverwendung anbetrifft, unter Python wird das auch hochgeehrt und umjubelt. In STL-Algorithmen wurschteln ist bei der Kürze wo genau das Problem? Wenn es keinen Nachteil erzeugt, aber ich die Lesbarkeit besser finde, halte ich das für besser (hier wieder: Geschmackssache). Und Schreibfaulheit ist keine schlechte Angewohnheit. Muss ich andauernd langen Code schreiben, geht die Entwicklungszeit hoch. Tut sie das, brauche ich für Aufgaben länger oder bewältige Aufgaben in gleicher Zeit schlechter. In jedem Fall ist mehr Schreibarbeit unökonomischer. D.h. nicht, dass man kurzen Code um jeden Preis ersuchen soll, aber wenig schreiben zu wollen liefert in meinen Augen den richtigen Anreiz.

    Und was paranoid angeht: Benutze das Wort bitte korrekt, wenn Du es schon verwendest. Selbst wenn man paranoid vom Vergleich auf den Vergleichenden überträgt, ergibt das keinen Sinn.

    Ach das war mehr Text als sinnvoll war. Mir liegt das doch eigentlich gar nicht am Herzen, das ist das Vormittagsloch (nicht so schlimm wie das Nachmittagsloch, aber trotzdem ein Loch :().



  • 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 👎



  • std::for_each sagt "wende folgende Funktion auf jedes Element an".
    range-based for loop sagt "führe folgendes Statement für jedes Element in der gegebenen Range aus".
    for loop sagt "nimm folgenden iterator und laufe so lange bis condition false wird".

    Man beachte z.B., dass man in std::for_each bzw. einer range-based for loop keinen Enfluss auf die Iteration nehmen kann.

    hustbaer schrieb:

    dot schrieb:

    Iteratoren sind in einem Debug Build per default checked, das kann man aber selbstverständlich deaktivieren, was im Release Build natürlich per default passiert...

    Ich bin mir 90% sicher dass die per Default immer checked sind.

    Das war iirc in VS 2005 so, wurde nach lautem Aufschrei der Community ("omgomgomg VS 2005 is laaaangsaaaam!!!!1111einseinseinself") aber schnell wieder geändert... 😉



  • 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 nicht auto&& ?



  • 1. WTF:

    Nexus schrieb:

    Warum decltype(determine_type(range)) und nicht auto&& ?

    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 nicht auto&& ?

    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_list

    Geht wunderbar. Mit GCC 4.9.0.


  • Mod

    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 nicht auto&& ?

    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.


Anmelden zum Antworten