Wieso operator + mit += implementieren??



  • otze schrieb:

    Du beschäftigst dich mit der funktionalität, ich mich mit der Kapselung (weshalb ich auch normalerweise friend operatoren vermeide btw) 😉

    Wenn du Kapselung nicht nur so als Begriff verwenden würdest, sondern dich mit dem Konzept dahinter beschäftigen würdest, würdest du feststellen, dass friend keinerlei negative Einwirkungen auf die Kapselung hat (nicht mehr als eine Methode auch). Besonders wenn es sich nicht um ein "long distance friendship" handelt.

    Und wehe, jetzt geht hier ein flamewar los, was kapselung mit dem OCP
    zu tun hat^^

    Es ging hier nicht um einen flamewar sondern um eine Diskussion. Aber mit dir zu diskutieren scheint eh sinnlos, da du nicht mal versuchst etwas sinnvolles beizutragen.



  • friend und encapsulation:
    http://www.parashift.com/c++-faq-lite/friends.html
    "Do friends violate encapsulation? No! If they're used properly, they enhance encapsulation."

    siehe auch: http://www.gotw.ca/gotw/070.htm
    "Get your interfaces right first. Internals are easy enough to fix later, but if you get the interface wrong you may never be allowed to fix it."

    http://www.gotw.ca/gotw/004.htm (class Complex)
    "operator+ should not be a member function. If it's a member like this, you can write "a=b+1" but not "a=1+b"."
    op+= als Member-Funktion, op+ wird außerhalb der Klasse realisiert und verwendet intern op+=:

    const Complex operator+( const Complex& lhs, const Complex& rhs ) 
    {
          Complex ret( lhs );
          ret += rhs;
          return ret;
    }
    

    Bei boost::rational wird op+= ebenfalls als Member-Funktion realisiert, op+ ist im header rational.hpp überhaupt nicht aufgeführt, soweit ich das erkennen kann. Muss irgendwie anders realisiert sein, konnte leider nicht genau erkennen wie. Vielleicht weiß das jemand genau?

    zu op+ und op+= siehe auch: http://coding.derkeiler.com/Archive/C_CPP/comp.lang.cpp/2003-10/2715.html

    otze hat übrigens dort Recht, wo er behauptet, dass Zähler und Nenner private sein sollten. Das war, soweit ich das erkennen konnte, sein Hauptziel. Er hat auch ausgeführt, dass op+ außerhalb der Klasse realisiert werden soll.

    Allgemein zum Optimieren:
    http://www-2.cs.cmu.edu/~gilpin/c++/performance.html#temporaries
    dort findet sich ebenfalls:
    "Temporaries can be avoided by using <op>= operators. For example, the code
    a = b + c;
    could be written as
    a=b;
    a+=c;."

    PS: Der nichtregistrierte Fragesteller hat hier einen Ton angeschlagen, der in keinster Weise zur Komplexität der Frage passt. Daher sollte man zumindest auch im Forum C++ nur registrierte Nutzer zulassen, damit dieser Mischung aus primitivem Getrolle (das jeder gut versteht und entsprechend emotional reagiert) und Fachchinesisch (das nicht jeder versteht, z.B. OCP, RVO, NRVO, ..., und daher Missverständnisse und Unklarheiten die Folge sind) ein baldiges Ende beschert sein möge. Dann sollte es wieder möglich sein, fachliche Fragen vorwiegend fachlich zu diskutieren. Humor ist allerdings erlaubt. 😉



  • Da ja nun geklärt ist, das man den op+ über op+= implementiert 🙂 hab ich mal 'ne Frage an die Profis. Zur Auswahl stehen zwei op+ Implementierungen:

    foo operator +(const foo& lhs, const foo& rhs)
    {
        return foo(lhs) += rhs;
    }
    
    foo operator +(foo lhs, const foo& rhs)
    {
        return lhs += rhs;
    }
    

    Variante 1 kann mein Compiler teilweise besser optimieren. Allerdings kann es uU Compilerfehler geben, wenn die Klasse aligned ist. Wohin schlägt also das Pendel: 1, 2 oder sollte man von Fall zu Falls unterscheiden?

    // edit
    Hoppla, da ist mir doch beim Kopieren ein dummer Fehler unterlaufen. 🙂



  • groovemaster2002 schrieb:

    Variante 1 kann mein Compiler teilweise besser optimieren. Allerdings kann es uU Compilerfehler geben, wenn die Klasse aligned ist.

    Kannst Du das genauer erklären?

    Ich verwende grundsätzlich Variante 1, weil sie ohne irgendwelche Tricks und Schnörkel einfach funktioniert. (+ ist normalerweise symmetrisch, also sollten auch die Parameter auf die selbe Art übergeben werden. Von einem Problem mit dieser Variante habe ich noch nie was gehört.

    MfG Jester



  • Variante 1 ist vorzuziehen:
    Wenn der Compiler entscheidet, die Funktion nicht zu inlinen, ist der Aufruf "billiger".

    Alignment? Wenn das Quellobjekt falsches Alignment hat, dann hast Du die Probleme sowieso beim erstellen der Kopie. Oder was meinst du?



  • peterchen schrieb:

    Variante 1 ist vorzuziehen:
    Wenn der Compiler entscheidet, die Funktion nicht zu inlinen, ist der Aufruf "billiger".

    Wo Du die Kopie ziehst ist doch letztlich egal, oder?



  • Jester schrieb:

    peterchen schrieb:

    Variante 1 ist vorzuziehen:
    Wenn der Compiler entscheidet, die Funktion nicht zu inlinen, ist der Aufruf "billiger".

    Wo Du die Kopie ziehst ist doch letztlich egal, oder?

    bei einem aufruf ja. bei 1000 aufrufen wäre ich geneigt, die kopie lieber im op+ zu ziehen, als 1000 mal außerhalb.



  • Einfach mal John Potters Erklärung lesen. Dazu noch den nächsten Beitrag von Daniel Frey.

    Variante 1 ist die schlechteste Alternative.
    Am Besten:

    const foo operator +(const foo& lhs, const foo& rhs)
    {
         foo rv(lhs);
         rv += rhs;
         return rv;
    }
    

    Das finde ich wirklich mal toll: Der Code, der am Besten zu lesen ist und am wenigsten nach "besonders-clever" aussieht, ist gleichzeitig auch der effizienteste.

    <edit>Tippfehler in Rückgabewert korrigiert!
    Natürlich liefert der op+ sein Ergebnis als Wert und nicht als Referenz.</edit>



  • referenz auf ein temporary?
    Klar, man spart sich eine Kopie, dafür muß man jedem Nutzer einbleuen, sofort eine Kopie zu ziehen... Effektiver, aber gefährlicher. Würde ich nicht als "am besten" bezeichnen.

    Ich seh auch an Herrn Potters Erklärungen a) keine anderen Varianten und b) keine Vorteile. Oder hab ich was übersehen?



  • peterchen schrieb:

    referenz auf ein temporary?

    tippfehler.



  • HumeSikkins schrieb:

    Einfach mal John Potters Erklärung lesen. Dazu noch den nächsten Beitrag von Daniel Frey.

    Variante 1 ist die schlechteste Alternative.
    Am Besten:

    foo& operator +(const foo& lhs, const foo& rhs)
    {
         foo rv(lhs);
         rv += rhs;
         return rv;
    }
    

    Hier wird doch ne Refernz auf ne lokale Var. zurückgegeben. Ist das nicht illegal?



  • interpreter und peterchens Beitrag, sowie dieser haben sich erledigt.



  • Ich dachte, Volkard hatte sich verschrieben 🙄 😉



  • HumeSikkins schrieb:

    Am Besten:

    foo& operator +(const foo& lhs, const foo& rhs)
    {
         foo rv(lhs);
         rv += rhs;
         return rv;
    }
    

    Ich hoffe dir ist aufgefallen das eine Warning geworfen wird bei dem code:
    main.cpp(54) : warning C4172: returning address of local variable or temporary



  • Ja, genau darüber wurde in den letzten 4 Beiträgen gesprochen. Es handelt sich um einen Tippfehler, das & im Typ des Rückgabewerts muß weg.



  • Jester schrieb:

    Ja, genau darüber wurde in den letzten 4 Beiträgen gesprochen. Es handelt sich um einen Tippfehler, das & im Typ des Rückgabewerts muß weg.

    lol sorry habs überlesen 😃



  • Hallo,
    sorry für die Verwirrung die ich durch diesen ärgerlichen Copy&Paste-Fehler gestiftet habe 😞
    Der Rückgabewert sollte natürlich const foo sein.
    Ich werde das jetzt korrigieren 🙂



  • Womit spätere Leser bei den Kommentaren und Zitaten verwirrt sein werden. 😛



  • Jester schrieb:

    groovemaster2002 schrieb:

    Variante 1 kann mein Compiler teilweise besser optimieren. Allerdings kann es uU Compilerfehler geben, wenn die Klasse aligned ist.

    Kannst Du das genauer erklären?

    Warum der Compiler das teilweise besser optimiert, kann ich dir nicht sagen. Aligned heisst einfach, dass Strukturen (bzw. Klassen) im Speicher an einer entsprechenden Grenze ausgerichtet werden. Das ist dann sinnvoll, wenn du zB SSE verwendest, wo teilweise aligned Adressen benötigt werden. Viele Compiler bieten dafür spezifische Schlüsselwörter (zB MSC __declspec(align(...))). Wird das Objekt also per Referenz übergeben gibt es keine Probleme. Muss der Compiler das hingegen auf dem Stack per Kopie ablegen, gibts Fehler, da er nicht weiss ob die Stackadresse immer noch dem Alignment entspricht.

    btw:
    Die Rückgabe als Referenz war natürlich falsch. Darf ich mal fragen, wieso Hume const foo als Rückgabetyp nimmt und nicht foo?



  • Weil sonst ich herkomme und schreibe:

    a + b = c;
    

Anmelden zum Antworten