Copy-Constructor und operator*



  • Hi,

    ich weiß zwar auch noch nicht, warum, aber wenn der operator*() eine const& zurückliefert, funktioniert's wie erwartet... (gcc)
    Hat vielleicht irgendwas mit "temporären Objekten" und/oder RVO zu tun ...

    Gruß,

    Simon2.



  • meine vermutung: du machst folgendes

    dummy a;
        dummy b;
        dummy c = dummy();
    

    was auch

    dummy a;
        dummy b;
        dummy c();
    

    ist, zwischendurch wird der *operator aufgerufen, aber der ist ohne jegliches belangen fuer den rest. fuehre mal ne neutzliche operation im operator durch.



  • rapso schrieb:

    meine vermutung: du machst folgendes

    dummy a;
        dummy b;
        dummy c = dummy();
    

    was auch

    dummy a;
        dummy b;
        dummy c();
    

    ist, zwischendurch wird der *operator aufgerufen, aber der ist ohne jegliches belangen fuer den rest. fuehre mal ne neutzliche operation im operator durch.

    Aber warum wird der CopyCtor genutzt, wenn operator*() eine "dummy const&" zurückgibt ?

    *in-den-wald-ruf*: CAAAAAAAMPER !!! Wir brauchen Dich hier ? 😉

    Gruß,

    Simon2.



  • Weil er nicht gerufen wird?

    int main() 
     { 
         dummy a; 
         dummy b; 
         dummy c( a * b);
         return 0; 
     }
    

    MfG Kimmi



  • eine classe mit einem konstruktor z.b. myclass(int)

    kann entweder mit
    myclass a(5)
    oder
    myclass a = 5 aufgerufen,

    darum wird der nicht aufgerufen



  • kimmi schrieb:

    Weil er nicht gerufen wird?

    int main() 
     { 
         dummy a; 
         dummy b; 
         dummy c( a * b);
         return 0; 
     }
    

    MfG Kimmi

    Und wo ist der Unterschied zu

    int main() 
     { 
         dummy a; 
         dummy b; 
         dummy tmp;
         tmp = ( a * b);
         dummy c = tmp;
         return 0; 
     }
    

    ... wo dann der CopyCtor auferufen wird ? Macht es soviel aus, wenn operator*() ein temporäres Objekt erzeugt ?

    Gruß,

    Simon2.


  • Mod

    Simon2 schrieb:

    Aber warum wird der CopyCtor genutzt, wenn operator*() eine "dummy const&" zurückgibt ?

    Denk mal über

    typedef const dummy const_dummy;
    int main()
    {
        dummy x = dummy();
        dummy y = const_dummy();
    }
    

    nach hinsichtlich der Voraussetzungen für die Auslassung des CopyOps.



  • camper schrieb:

    ...der Voraussetzungen für die Auslassung des CopyOps.

    Leider kenne ich nicht "die Voraussetzungen für die Auslassung des CopyOps"...

    ... und warum der CopyCtor ausgelassen wird, weiß ich auch nicht.

    Gruß,

    Simon2.



  • Für mich riecht das ganz klar nach RVO. Eigentlich sollte ein Compiler bei dem Code mit ausgeschalteten Optimierungen einen Aufruf des copy-ctor erzeugen, aber egal.



  • hustbaer schrieb:

    ...aber egal.

    Finde ich jetzt nicht .... immerhin schien camper eine Lösung zu haben (die er leider noch nicht aufgedeckt hat).
    Außerdem sollte RVO doch Seiteneffekte erkennen/berücksichtigen .. das tut sie aber im vorliegenden Fall nicht (habe mal spaßeshalber einen CopyCtor mit Seiteneffekt gebastelt und der wird auch nicht aufgerufen).
    Und schließlich ist es IMO kein sooo exotischer Fall, dass man sowieso nicht in diese Falle tappen könnte.

    Wäre schon schön, wenn hier klar würde, wo der (Denk-)fehler liegt.

    Gruß,

    Simon2.



  • Ich nehme an RVO ist eine selbstverständliche Optimierung und keine, die man je nach Bedarf einschalten können soll (wäre imo auch logisch gesehen gut).

    Was das Beispiel angeht, hat Simon2 ja schon gemutmaßt, dass es sich um RVO handelt. Hier wird das Objekt tmp (welches im operator* erzeugt und zurückgegeben wird) direkt in c hineinkonstruiert, es wird also kein Copy-Ctor benötigt.

    Dummy c = a*b; // äquivalent zu
    Dummy c( a*b ); // äquivalent zu
    Dummy c( a.operator*( b ) );
    
    // Pseudocode für operator*
    Dummy operator*( Dummy const& left, Dummy const& right ) 
        returns: Dummy tmp;
    {
    }
    
    // Ablauf nach RVO:
    Dummy c; 
    operator*( c, a, b );
    void operator*( Dummy& target, Dummy const& left, Dummy const& right )
    {
    }
    

    Dummy "c" ist also Dummy "tmp", weshalb nirgendwo etwas kopiert werden muss.

    Bei der Rückgabe einer (konstanten) Referenz hingegen sollte klar sein, dass die Möglichkeit nicht besteht, die Initialisierung des Objekts in den Operator zu verlegen, da eine Rückgabe einer Referenz auf ein lokales Objekt undefiniert wäre. Demnach muss das zurückgegebene Objekt ausserhalb der Funktion existieren (this-Objekt, global, jedoch nicht im Aufrufer, es seidenn der Operator gibt einen der Operanden zurück ...), wodurch dessen Konstruktion nicht von der Funktion in den Aufrufer verlegt werden kann, wodurch eine Kopie erforderlich wird.

    Dummy c( a*b ); // Kopie erforderlich
    Dummy const& operator*( Dummy const& left, Dummy const& right )
    {
        return somethingNotLocal;
    }
    

    PS: Dieser Beitrag dient auch für mich selbst dazu, die Zusammenhänge auseinanderzuklamüsern. Sollte hier erwiesenermassen Quatsch stehen würde ich Euch (camper) bitten, das möglichst klarzustellen 😉



  • Hi,

    mir scheint's auch immer mehr RVO zu sein...

    allerdings bin ich ein wenig beunruhigt, weil es mein Verständnis von der Konstruierei doch ein wenig durcheinanderwirbelt.

    Letztlich bleibt:

    dummy f() { ... };
    
    int main() {
       dummy a = f();
    ...
    

    In dem Fall bestimmt nicht "der Erzeuger von a", welcher Konstruktor aufgerufen wird (der evtl. erwartete CopyCtor), sondern der Implementierer von f().
    Das beschränkt ein wenig die Möglichkeiten des dummy-Entwicklers, weil hier RVO wohl letztlich von einer "Standard-Copy-Konstruktion" (also Objekte sind hinterher im Speicher identisch) ausgeht und irgendwelche exotischen Seiteneffekte (fällt mir gerade kein Beispiel ein; Referenzcounting würde sich daran ja nicht stören) nicht einbauen kann.
    Naja, immerhin kann er das Kopieren noch ganz verbieten ...

    Hmmm strange....

    Gruß,

    Simon2.



  • Sieh es doch mal so.Die Compiler sind jetzt so intelligent, dass sie Dinge optimieren an die du nie denken würdest, egal ob du das so willst oder nicht. 🙂
    Ich hatte den Test auch gemacht und mich schon gewundert, das der g++ im debugmodus den CopyCTor rausoptimiert. Sobald im Operator* etwas vernünftiges stand war er aber wieder drin.



  • Hi,

    was war denn "etwas Vernünftiges" ?

    Gruß,

    Simon2.



  • dummy operator*( const dummy & other )
    {
        //code
        cout << "Operator*" << endl;
        dummy tmp(other); //lokales Obj für Matrixmultiplikation
                    // mache multiplikation
        return tmp;
    };
    


  • Braunstein schrieb:

    dummy operator*( const dummy & other )
    {
        //code
        cout << "Operator*" << endl;
        dummy tmp(other); //lokales Obj für Matrixmultiplikation
                    // mache multiplikation
        return tmp;
    };
    

    Naja, den CopyCtor explizit aufrufen, kann ja jeder.... 😉
    ... und RVO findet ja trotzdem statt.

    Mein "Vernünftiges" sah nämlich so aus:

    class dummy {
       int i;
    // ...
    };
    
    dummy operator*( const dummy & other )
    {
        //code
        cout << "Operator*" << endl;
        dummy tmp;
        tmp.i = i * other.i;
        return tmp;
    };
    

    .... und da wird KEIN CopyCtor aufgerufen, obwohl es mir als sehr vernünftige Implementation dieses operators erschien... 😃

    Gruß,

    Simon2.



  • Das sehe ich auch so. ....... jetzt! 😃
    Obwohl die Verwendung des CopyCtor hier den weiteren Aufruf eines Default-Konstrukltors bedingen würde (für das temporäre Objekt). Sonst gäbe es ja nichts zum kopieren. Und genau das scheint der Compiler ja wegzuoptimieren.
    Ich gebe zu, der direkte Aufruf des CopyCTor war kein so gutes Beispiel.



  • Simon2 schrieb:

    Außerdem sollte RVO doch Seiteneffekte erkennen/berücksichtigen ..

    Wäre mir neu. Wie kommst du darauf?
    RVO ist nur eine der ganzen Stellen wo der Programmierer nicht genau wissen kann wo bzw. wie oft seine Objekte rumkopiert werden...

    Ein Copy-Ctor muss - damit eine Klasse in C++ sinnvoll einsetzbar ist - immer so geschrieben sein dass

    Foo a = x;
    

    zu

    Foo tmp = x;
    Foo a = tmp;
    

    äquivalent ist -- also was x und a angeht.



  • Also ich verwende VS2005.NET Standardedition.
    Und wenn ich

    #include <iostream> 
    
    using namespace std; 
    
    class dummy 
    { 
        public: 
            dummy() 
            { 
                cout << "Default-Constructor" << endl; 
            }; 
            dummy( const dummy& other ) 
            { 
                cout << "Copy-Constructor" << endl; 
                            //mache deep copy 
            }; 
            dummy operator*( const dummy & other ) 
            { 
                //code 
                cout << "Operator*" << endl; 
                dummy tmp; //lokales Obj für Matrixmultiplikation 
                            // mache multiplikation 
                return tmp; 
            }; 
            dummy& operator=(const dummy& other) 
            { 
                cout << "Operator=" << endl; 
    
                            //erstelle deep-copy etc. 
    
                return *this; 
            }; 
    }; 
    
    int main() 
    { 
        dummy a; 
        dummy b; 
        dummy c = a * b; 
        return 0; 
    }
    

    mit den Standardeinstellungen compiliere, sieht die Ausgabe so aus:

    Default-Constructor
    Default-Constructor
    Operator*
    Default-Constructor
    Copy-Constructor

    Also bei mir wird der Copykonstruktor demnach aufgerufen oO.



  • Kahino schrieb:

    Also ich verwende VS2005.NET Standardedition.
    Und wenn ich [...] mit den Standardeinstellungen compiliere [wird bei mir] der Copykonstruktor ... aufgerufen oO.

    Hast Du im Debug- oder im Release-Modus kompiliert? Im Release-Modus ist der Standard für die Optimierungen in VS 2005 /O2, also Optimierung auf Geschwindigkeit. Bei Debug-Modus hingegen werden standardmäßig keine Optimierungen vorgenommen.


Anmelden zum Antworten