Pre- oder Post-Inkrementierung bei for-Schleifen?



  • Hacker schrieb:

    Belli schrieb:

    i++;
    drückt aus: Erhöhe um eins - und ist damit identisch zu ++i (vom Ausdruck her);

    Nein. i++ bringt schon bei Integralen (wenn auch minimalen) Kopieraufwand (ist gleichzusetzten mit (++i - 1)).

    NEIN!

    PI hat schon Recht, es ist üblich (und gerade, wenn die Inkrementierung in nur einer Anweisung steht), das Präfix zu nehmen.

    Auja, immer lustig wenn die Kids einem sagen was üblich ist.
    Alter Schwede, wie kann man nur so von sich eingenommen sein.

    Leute, Newsflash: üblich ist i++!
    Wer was anderes glaubt lebt in einer Traumwelt.



  • Aber wen juckts wenns aufs selbe hinausläuft?
    Weil für mich code spricht, ist ++i zu bevorzugen wenn nur ein Wert, i, gebraucht wird, und i++ wenn zwei Werte, i und i + 1, gebraucht werden. Wer es nicht so hält wird trotzdem glücklich, Leute.

    Edit: Wir sollten lieber 3 Seiten threads über den letzten ineffizienten Algorithmus haben, den wir so verbrochen haben, nicht ob man prä oder postfix inkrementiert.



  • 314159265358979 schrieb:

    Die viel wichtigere Begründung ist Ausdruck. Damit meine ich, was der Code auf den Leser ausdrückt. ++i drückt aus "erhöhe um 1". i++ drückt aus "erhöhe um 1 und gib mir den Wert vor der erhöhung, weil ich diesen noch brauche". Damit sollte auch klar sein, warum man ++i verwenden sollte.

    Nach der Erklärung isses bei der Schleife doch egal, weil der zurückgegebene Wert interessiert in diesem Fall nicht 😕



  • Mit Iteratoren ist Prefix schneller, also kann man es sich schon angewöhnen diesen in einer for-Schleife zu verwenden.



  • hustbaer schrieb:

    Leute, Newsflash: üblich ist i++!
    Wer was anderes glaubt lebt in einer Traumwelt.

    Kann ich aus meiner Erfahrung nicht bestätigen. Im jüngeren Code hier bei uns in der Firma sieht man das Postinkrement eigentlich nur noch wenns wirklich so benötigt wird, also sehr selten. Dann hab ich gerade mal in Effective C++ 3rd ed. und C++ Templates the complete guide geblättert bis ich auf das erste Inkrement gestoßen bin. Bei Meyers war das erste, das ich sah, Item 29 "++imageChanges;" und im anderen wars auf Seite 37 "++numElems;", also auch beides Prefix.
    Auch wenn in anderem code Prefix vielleicht nicht üblig ist, halte ich es schon für sinnvoll, das für sich selbst erstmal als Default zu nehmen und es so einfach üblich zu machen, weil es eigentlich nie schlechter sein sollte, dafür aber manchmal besser ist. Und bei Template-Kram weiß man ja eh nicht mehr, was man da gerade überhaupt erhöht. 😉



  • Hacker schrieb:

    Nicht um den heißen Brei herumreden. ++i ist, gerade bei größeren Klassen, performanter.

    Das musst du mir erst mal durch Messungen beweisen. Und ich Wette mit dir, dass wenn du einen Fall findest bei dem das wirklich zutrifft, die Klasse noch ganz andere Probleme hat.

    Gegenhypothese: es macht absolut 0 Unterschied, solange der Typ etwas so anspruchvolles wie ein iterator ist.



  • otze schrieb:

    Hacker schrieb:

    Nicht um den heißen Brei herumreden. ++i ist, gerade bei größeren Klassen, performanter.

    Das musst du mir erst mal durch Messungen beweisen. Und ich Wette mit dir, dass wenn du einen Fall findest bei dem das wirklich zutrifft, die Klasse noch ganz andere Probleme hat.

    Gegenhypothese: es macht absolut 0 Unterschied, solange der Typ etwas so anspruchvolles wie ein iterator ist.

    Na, dann lügt Ethon schonmal. Oder du hast wahrscheinlich ein "nicht" vergessen.



  • Ich glaube, dass Ethon exakt so viel gemessen hat wie du.



  • otze schrieb:

    Ich glaube, dass Ethon exakt so viel gemessen hat wie du.

    Ich habe recht viele Iteratoren mit Shared Pointern darin.
    Jedes Postfix-Inkrementieren würde von daher ein Ref-Plus und ein Ref-Minus bedeuten. Und da der Refcounter threadsicher manipuliert wird ist da schon etwas Overhead dabei.

    Ich könnte das messen, der restliche Overhead würde aber den Unterschied schlucken (denke ich mal). Aber hey, wieso die Taktzyklen wegwerfen wenn es sie gratis gibt. Man muss kein Zeichen mehr tippen, einfach nur Reihenfolge vertauschen.



  • *seufz* da sBringt und so nicht weiter.

    Hier mal ein Argument.

    Angenommen, unser Typ ist ein normaler, halbwegs sinnvoll implementierter Iterator.

    Dann wird er in keinem Fall komplizierter sein als:

    class iterator{
    private:
        T* pointer1,....,pointerN;
        std::size_t pos1,....,posN;
    public:
        //ich benutze mal einfach namen, weil ich mir nie merken kann, wie die
        //pre/postincrement syntax ist
        inline iterator& preinc();
        inline iterator postinc();
    };
    

    angenommen, preinc ist sehr komplex, dnan schauen wir uns die implementierung nicht an. Wir wissen aber, wie dann postinc aussieht:

    iterator postinc(){
        iterator copy=*this;
        preinc();
        return copy;
    }
    

    nun sind wir in einer solchen Situation:

    for(iterator i=...; i !=...; i++){
    }
    

    was passiert?
    Der Compiler wird postinc inlinen. Dann wird er merken, dass die Kopie sinnlos ist(und keine Seiteneffekte hat). Dann sagt ihm der Standard, dass er die Kopie raus optimieren kann. Übrig bleibt preinc();

    //edit ich sags doch: iterator mit shared_ptr. Wozu?!?



  • Heisst das jetze C++ ist garnicht so toll?


  • Mod

    Wieder mal ein schönes Beispiel dafür, dass die Länge einer Diskussion umgekehrt proportional zu deren Bedeutung ist.



  • camper schrieb:

    Wieder mal ein schönes Beispiel dafür, dass die Länge einer Diskussion umgekehrt proportional zu deren Bedeutung ist.

    👍



  • //edit ich sags doch: iterator mit shared_ptr. Wozu?!?

    Sobald man mit etwas abstrakterem als rohem Speicher arbeitet gehts fast nicht anders. Besonders wenn man mit OS/C-Schnittstellen arbeitet hat man oft Daten, die nicht trivial kopierbar sind. Die C++-Algorithmen erfordern aber alle dass Iteratoren kopierbar sind und die Kopien auch eigenständig funktionieren.

    Simples Beispiel: Wie würdest du einen Iterator implementieren, der eine Datei zeilenweise iteriert? Ohne Interna nach außen zu ziehen?
    Prominentes Beispiel: Boost.Filesystem, der path_iterator nutzt intern boost::shared_ptr.



  • Ethon schrieb:

    Simples Beispiel: Wie würdest du einen Iterator implementieren, der eine Datei zeilenweise iteriert? Ohne Interna nach außen zu ziehen?
    Prominentes Beispiel: Boost.Filesystem, der path_iterator nutzt intern boost::shared_ptr.

    Hmm stimmt. An virtuelle Objekte (im Sinne von: nicht wirklich vorhanden) habe ich da nicht gedacht. Ich würde aber auch in dem Fall behaupten, dass die Operationen die mit den Iteratoren stattfinden an sich schon so teuer sind, dass man auch in dem Fall mit Messungen keinen Unterschied feststellen würde.

    Die Frage bei so etwas ist: ist das Aufwand/Nutzen Verhältnis hoch genug, als dass man in irgendeinem Coding Standard schreiben müsste: "übrigens, wir verwenden nur pre-increment in for-Schleifen". So etwas muss umgesetzt und durchgesetzt werden. Und in 99% der Fälle macht die Unterscheidung keinen Unterschied(in meinem Projekt exakt 100%). Da würde ich jetzt auch keinem Anfänger sagen: "++i ist besser", nur damit er dann bei jeder for-schleife anfängt nachzudenken, welche Version denn nun nochmal die Schnellere war. Die Wahrscheinlichkeit ist hoch, dass es a) egal ist und b) die Aufmerksamkeit nur von viel schlimmeren Fehlern geklaut wird.



  • Das ist halt so eine Sache, zu der es ein ganzes Spektrum von Standpunkten gibt.
    <- "Pre als default verpflichtend im Firmenstyleguide."(1) <-> "Pre für mich persönlich als default, weil ich dann weniger nachdenken muss."(2) <-> "Ganz egal."(3) <-> "Post als Default weils für mich semantisch irgendwas aussagt."(4) ->
    Dass du gegen 1 argumentierst bedeutet natürlich nicht, dass du für 4 bist. Ich steh bei 2, und du?

    Edit: Ah, jetzt hab ichs, glaub ich. Selbst wenn du beispielsweise bei 2 stündest, könntest du einen Anfänger ja trotzdem damit verschonen. Ich würd in einem Tutorial oder so vermutlich die Postfix-Variante erstmal gar nicht (bzw. nur als Anmerkung) erwähnen und einfach überall Prefix benutzen. Das sollte für den Neuling dann recht einfach sein und auch die, die sich mit komplexeren Iteratoren auskennen (und den von dir erklärten Optimierungen nicht immer trauen, oder es einfach auch schon ohne Optimierung optimal haben wollen), nicht stören. 🙂



  • @Dobi:
    Also erstmal fehlt mir ein Punkt "post für mich als Default weil ich keinen Grund sehe mich umzugewöhnen" (3.5), und dann ist (4) auch noch nicht das Gegenstück zu (1).
    Also "Post als default verpflichtend im Firmenstyleguide." (5)

    Ich = (3.5)



  • camper++ schrieb:

    camper schrieb:

    Wieder mal ein schönes Beispiel dafür, dass die Länge einer Diskussion umgekehrt proportional zu deren Bedeutung ist.

    👍

    👍

    Japp. Sieht man schon an ALLEN laengeren Threads im RudP. Und wenn Kleinkinder wie Pi und Hacker mitposten, sinkt das Niveau rapide gegen Null.



  • Kommt es nicht auch darauf an wo man i++/++i benutzten will ?Jenachdem kann man das dann benutzen wie man will.



  • 7xCore schrieb:

    Kommt es nicht auch darauf an wo man i++/++i benutzten will ?Jenachdem kann man das dann benutzen wie man will.

    Ja natürlich, immerhin sind es 2 unterschiedliche Operatoren. Immer den Prefix-Operator zu benutzen ist doch nur darauf bezogen, wenn du für den zurückgegebenen Wert überhaupt gar keine Verwendung hast.


Anmelden zum Antworten