end-Iterator decrement definiert?



  • Nathan schrieb:

    Ihr diskutiert wirklich, wie man über ein Range iteriert?
    Ich mein, die Antwort ist doch klar! Der einzige wahre Weg ist:

    void iterate(iterator begin, iterator end)
    {
      (do_sth(*begin), begin != end) && (iterate(++begin, end), true);
    }
    

    Ich bitte euch!

    UB wenn begin==end. Ich sags dir, über einen Range zu iterieren ist nicht trivial, deshalb ist man froh, die Funktion range() zu haben.



  • c++11-coder schrieb:

    Nathan schrieb:

    Ihr diskutiert wirklich, wie man über ein Range iteriert?
    Ich mein, die Antwort ist doch klar! Der einzige wahre Weg ist:

    void iterate(iterator begin, iterator end)
    {
      (do_sth(*begin), begin != end) && (iterate(++begin, end), true);
    }
    

    Ich bitte euch!

    UB wenn begin==end. Ich sags dir, über einen Range zu iterieren ist nicht trivial, deshalb ist man froh, die Funktion range() zu haben.

    Stimmt, falsche Reihenfolge

    begin != end && (do_sth(*begin), iterate(++begin, end), true);
    

    Mein Fehler.



  • Schade, ich dachte schon ihr wärt an einer ernsthaften Diskussion interessiert.

    @Arcoth: Bitte bleib beim konkreten Beispiel und sag, wo dort die Vorteile an deiner Version liegen. Dein Stellvertreter-Argument kann ich so nicht übertragen. Ich denke aber es wäre einfacher, nachzugeben 😉



  • Nathan schrieb:

    Stimmt, falsche Reihenfolge

    begin != end && (do_sth(*begin), iterate(++begin, end), true);
    

    Mein Fehler.

    Immer noch UB, allerdings sehr subtil: do_sth könnte einen Typ zurückgeben, der den operator, überladen hat. Dann ist do_sth(*begin) nicht mehr vor iterate(++begin, end) gesequenced, was bedeutet, dass *begin und ++begin nicht gesequenced sind, was dazu führen kann, dass do_sth(*(begin+1)) aufgerufen wird, was einerseits falsch ist und andereseits UB für begin+1==end.
    </OT>

    Nexus schrieb:

    Schade, ich dachte schon ihr wärt an einer ernsthaften Diskussion interessiert.

    Bin ich. Ich finde meine range-Variante kürzer und lesbarer.

    Und zwar aus dem Grund, dass es auf den ersten Blick genauso kurz verständlich ist wie die explizite for-loop, aber man bei der for-Loop immer noch einen kurzen Check machen muss, ob die Form wirklich stimmt. Wie schnell merkst du, dass bei den folgenden Schleifen was faul ist:

    for( unsigned i = 1; i == 20; ++i )
    
    for( unsigned i = 1; i != 20; --i )
    


  • Sorry, Nexus aber:

    c++11-coder schrieb:

    Nathan schrieb:

    Stimmt, falsche Reihenfolge

    begin != end && (do_sth(*begin), iterate(++begin, end), true);
    

    Mein Fehler.

    Immer noch UB, allerdings sehr subtil: do_sth könnte einen Typ zurückgeben, der den operator, überladen hat. Dann ist do_sth(*begin) nicht mehr vor iterate(++begin, end) gesequenced, was bedeutet, dass *begin und ++begin nicht gesequenced sind, was dazu führen kann, dass do_sth(*(begin+1)) aufgerufen wird, was einerseits falsch ist und andereseits UB für begin+1==end.
    </OT>

    Die Tricks vom IOCCC funktionieren wohl wirklich nur mit C.

    Nexus schrieb:

    Schade, ich dachte schon ihr wärt an einer ernsthaften Diskussion interessiert.

    Bin ich. Ich finde meine range-Variante kürzer und lesbarer.[...]

    Absolut deiner Meinung.


  • Mod

    Ich denke aber es wäre einfacher, nachzugeben

    Jup, ich gebe nach. War alles Blödsinn. Auch wenn ich while-Schleifen einfach deutlich mehr mag, als for-Schleifen. Ich mag es halt nicht, wenn mehrere Dinge auf eine Zeile fallen, dann müssen schon viele Leerzeichen dabei sein...


  • Mod

    Nathan schrieb:

    Sorry, Nexus aber:

    c++11-coder schrieb:

    Nathan schrieb:

    Stimmt, falsche Reihenfolge

    begin != end && (do_sth(*begin), iterate(++begin, end), true);
    

    Mein Fehler.

    Immer noch UB, allerdings sehr subtil: do_sth könnte einen Typ zurückgeben, der den operator, überladen hat. Dann ist do_sth(*begin) nicht mehr vor iterate(++begin, end) gesequenced, was bedeutet, dass *begin und ++begin nicht gesequenced sind, was dazu führen kann, dass do_sth(*(begin+1)) aufgerufen wird, was einerseits falsch ist und andereseits UB für begin+1==end.
    </OT>

    Die Tricks vom IOCCC funktionieren wohl wirklich nur mit C.

    cast nach void hilft.



  • c++11-coder, kannst du etwas mehr über range() verraten? Basiert das auf Boost.Range oder hast du das selbst gestrickt?

    Ich frage mich vor allem deswegen, weil

    for (auto e : r)
    

    davon ausgeht, dass für den Typ von r ein begin() und end() (global oder als Member) vorhanden ist, und diese beiden Funktionen jeweils Iteratoren zurückgeben.

    Damit aber so etwas möglich wäre wie in der verschachtelten Schleife, wo e selbst ein Iterator (und kein Element) ist, müsstest du eine zusätzliche Indirektion einbauen. Machst du das tatsächlich oder war es mehr eine Idee?



  • Ich habe mir mein range() zwar selber geschrieben, aber neu ist die Idee nicht. Du könntest auch boost::irange verwenden:

    #include <iostream>
    #include <string>
    #include <boost/range/irange.hpp>
    
    int main()
    {
      for (int i : boost::irange(0,5))
        std::cout << i << '\n';
    
      std::string s = "abcdef";
      using std::begin; using std::end;
      for (auto i : boost::irange(begin(s), end(s)))
        for (auto j : boost::irange(std::next(i), end(s)))
          std::cout << *i << *j << '\n';
    }
    

    Ausgabe:

    0
    1
    2
    3
    4
    ab
    ac
    ad
    ae
    af
    bc
    bd
    be
    bf
    cd
    ce
    cf
    de
    df
    ef
    

    Dieser range() ist eine schöne Ergänzung zum normalen range based for, weil das for eine Indirektion wegnimmt, man aber manchmal die Iteratoren eben doch braucht. Und es funktioniert super mit int-Ranges.



  • Arcoth schrieb:

    Ich denke aber es wäre einfacher, nachzugeben

    Jup, ich gebe nach. War alles Blödsinn. Auch wenn ich while-Schleifen einfach deutlich mehr mag, als for-Schleifen. Ich mag es halt nicht, wenn mehrere Dinge auf eine Zeile fallen,

    Du magst for nicht so gern, NUR WEIL Du for mißbrauchst, Alda!


  • Mod

    volkard schrieb:

    Du magst for nicht so gern, NUR WEIL Du for mißbrauchst, Alda!

    Jetzt halt schon die Schnauze mit deinem Assi-Dialekt! Bist du erst 25 oder was?

    Und ich missbrauche for -Schleifen nie! Ich nutze sie tatsächlich lediglich für das Standardschema. Beispiel:

    for( unsigned i = 2; i != primes.size(); ++i )
    

    Für nichts anderes. Außer vielleicht

    for( unsigned i; std::cin >> i; )
        //...
    

    Wobe ich da schon eher zu while tendiere.



  • Arcoth schrieb:

    Und ich missbrauche for -Schleifen nie! Ich nutze sie tatsächlich lediglich für das Standardschema. Beispiel:

    for( unsigned i = 2; i != primes.size(); ++i )
    

    Für nichts anderes.

    Aha...

    Arcoth schrieb:

    for (auto first = begin(v); first != end(v); ++first) // Hier sollte die Abbruchbedingung wahrscheinlich eher first != --end(v) lauten
    	for( auto second = first; ++second != end(v); )
    		doSomething( *first, *second );
    

    Das ist mMn. deutlich eleganter.



  • Arcoth schrieb:

    Allerdings kann man dieses temporäre Objekt gleich selbst in die Hand nehmen

    …
    	for( auto second = first; ++second != end(v); )
    		doSomething( *first, *second );
    

    Das ist mMn. deutlich eleganter.

    😮 😮 😮


  • Mod

    Aha...

    Ups! Ich sollte es natürlich nicht so formulieren. Ich nutze es auch zum Iterieren mit Iteratoren. Standardschema hieß oben
    for( Initialisieren; Vergleich; Inkrement )
    wie immer.

    Außerdem habe ich nicht gesagt, dass

    for( auto second = first; ++second != end(v); )
    

    die eleganteste Methode ist. Es ist nur so: Ich finde es schöner als

    for( auto second = std::next( first ); second != end(v); ++second )
    

    ,
    aber wahrscheinlich würde ich in meinem Code eher while benutzen. Und zwar so

    auto second = first;
    while( ++second != end(v) )
    

    Weil ich for, sobald es von obigem Schema abweicht, einfach nicht mehr attraktiv finde. Bei while kann die Bedingung auch gut und gerne ein wenig komplizierter werden.

    Nun fand ich bei dem konkreten Beispiel doof, dass man die Klammern ergänzen muss, wenn man while verwendet (weil man noch eine Zeile zum Definieren von second braucht)! Ich versuche immer möglichst ohne diese Klammern auszukommen - das ist ein Tick von mir 🤡



  • Arcoth schrieb:

    for( auto second = std::next( first ); second != end(v); ++second )
    

    Ein Bißchen Syntax-Rauschen weg

    for( auto second=next(first); second!=end(v); ++second )
    

    und schon isses hübsch. 🤡
    Nee, mal ehrlich, eine komplexte Laufbedingung nervt Dich weniger als eine komplexe Initialisierung, das finde ich irgendwie C.



  • Arcoth schrieb:

    ...

    for( auto second = first; ++second != end(v); )
    

    Man sollte nur unter zwingenden Gründen von der Standard-Funktionalität der drei for -Argumente abweichen. Das "clevere" ++second ist eine versteckte Bombe für alle anderen (und dich selbst in 3 Jahren), die mit dem Code zu tun haben: Man muss erst einmal die volle Tragweite des ++ an dieser Stelle erfassen! Der Großteil der Leser wird auf den ersten Blick wohl nicht korrekt erfassen, das die Schleife tatsächlich tut.

    Geht in Richtung "Principle of least surprise". Da muss das persönliche Schönheitsempfinden manchmal weichen, vor allem, wenn man nicht allein entwickelt.

    Arcoth schrieb:

    auto second = first;
    while( ++second != end(v) )
    

    ... einfach nicht mehr attraktiv finde.

    Dito. Jedes Handbuch über guten Code sagt, dass for nicht nur eine Art von Schleife ist, sondern bereits deren Aufgabe beschreibt, nämlich über eine bestimmte Menge der Reihe nach zu iterieren. Allein durch dieses Schlüsselwort hat der Leser bereits mehr Information als bei einem simplen while . Du ziehst eine Mutter auch nicht mit der Kombizange an, wenn du den passenden Maulschlüssel hast, oder?

    Dazu kommt noch was anderes: second steht außerhalb des Scopes der Schleife, bei for innerhalb.


  • Mod

    Arcoth schrieb:

    auto second = first;
    while( ++second != end(v) )
    

    ... einfach nicht mehr attraktiv finde.

    Dito.

    Lern lesen und zitieren, Junge.
    Edit: Oder nur zitieren. Ich weiß nicht genau, zu was du zustimmst, aber das Zitat ist falsch.

    Das "clevere" ++second ist eine versteckte Bombe für alle anderen (und dich selbst in 3 Jahren), die mit dem Code zu tun haben:

    Ich werde auch in dreißig Jahren sofort sehen, was ++second da verloren hat, da bin ich mir verdammt sicher 😉



  • Arcoth schrieb:

    Lern lesen und zitieren, Junge.

    Lern vielleicht ein bisschen den Ball flachzuhalten. Wir sind hier nämlich nicht mehr 25.

    Es geht um die Verbindung von Schönheitsempfinden und "attraktiv finden". Zitat vielleicht missglückt, aber das war der Hintergrund. Dass es darum geht, den Code auch für andere so klar wie möglich zu schreiben.

    Arcoth schrieb:

    Ich werde auch in dreißig Jahren sofort sehen, was ++second da verloren hat, da bin ich mir verdammt sicher 😉

    Du vielleicht. Und deine Kollegen?



  • minastaros schrieb:

    Jedes Handbuch über guten Code sagt, dass for nicht nur eine Art von Schleife ist, sondern bereits deren Aufgabe beschreibt, nämlich über eine bestimmte Menge der Reihe nach zu iterieren. Allein durch dieses Schlüsselwort hat der Leser bereits mehr Information als bei einem simplen while .

    Jo. Sehe ich in sonst feinem ein for, weiß ich, daß über die ganze Sequenz iteriert wird. Welche Sequenz ist meist eh aus dem Kontext klar. Da bereite ich mich schon auf den Kern der Schleife vor, während ich der Vollständigkeit halber noch Initialisierungsausdruck lese, und spätestens dann weiß ich, daß es die Schleife war, die ich meinte und steige in den Körper ein, während ich noch halbherzig überfliege, daß die Laufbedingung stimmt und den Weiterschaltungsausdruck, kann ja nur ++i oder p=p->next oder so sein, falls nicht, fällt es mir nachträglich auf, obwohl die Augen schon unten sind: "Da hat doch gerade was nicht gestimmt?!", dann schalte ich vom 5. Gang in den 1. Gang zurück und habe einen Getriebeschaden. Und der ganzen Funktion oder der ganzen Datei stehe ich ein Weilchen mit Mißtrauen gegenüber.


  • Mod

    Du vielleicht. Und deine Kollegen?

    Welche Kollegen? Ich arbeite mit niemandem, und das habe ich auch noch nie (das erklärt einiges, was?).

    Ich würde nicht in einem Projekt Code schreiben, der nicht gängigen Idiomen folgt. Vor allem gibt es in (so gut wie) allen Firmen und größeren Projekten ganz feste Regeln, nach denen Code geschrieben wird - von der Formatierung bis eben zu Dingen wie for-Schleifen. Da dürfte ich solche Schleifen möglicherweise nicht schreiben.

    Auch wenn ich nach wie vor nicht verstehe, wie man diese beiden Zeilen

    unsigned i = 0;
    while( ++i < 10 )
    

    Nicht sofort verstehen kann. Ist doch nicht so schwer. Strengt ihr euch beim Code lesen nicht an? Auch wenn ich persönlich natürlich für Skalare

    for( unsigned i = 0; i != 10; ++i )
    

    schreibe, und zwar immer. Bei Skalaren nutze ich praktisch nur for -Schleifen. Für so etwas schreibe ich nicht einmal eine while-Schleife, wenn ich die Schleifenvariable nachher brauche, dann schreibe

    unsigned i = 0;
    for(; i != 10; ++i )
    

    Das ist alles eindeutig.

    Lern vielleicht ein bisschen den Ball flachzuhalten. Wir sind hier nämlich nicht mehr 25.

    Tut mir Leid. 🙂
    (Die Meisten hier sind übrigens recht jung, höchstens 30)

    Da muss das persönliche Schönheitsempfinden manchmal weichen

    Wirklich? Das glaube ich gar nicht. Nämlich dass mein Schönheitsempfinden von dem der Menge abweicht. Ich versuche möglichst lesbaren Code zu schreiben, gerade bei Schleifen. Und ich erwarte nichts, wenn ich Code lese, jede Zeile kann einem völlig neuem Muster folgen.

    Vor allem hatte ich nicht erwartet, dass man (bspw. Volkard) einen Getriebeschaden erleidet, wenn die Schleife im Code nicht aussieht wie 99% der anderen. Das muss ich mir auf jeden Fall merken. 👍

    kann ja nur ++i oder p=p->next oder so sein,

    Moment mal! Für so etwas verwendet man doch keine for-Schleife! Bei Listenknoten sollte das doch definitiv ans Ende des Körpers ausgelagert werden. So habe ich es aufgefasst, nachdem du auf Schleifen so penibel achtest.

    Nee, mal ehrlich, eine komplexte Laufbedingung nervt Dich weniger als eine komplexe Initialisierung, das finde ich irgendwie C.

    Was genau daran assoziierst du mit C?


Anmelden zum Antworten