Implizite Konvertierung von Objektfunktionen wie?



  • Es sei eine gegeben eine Klasse

    class K 
    {
      public:
        void foo(int i) { /** mach was **/ };
    }
    
    int main()
    {
      K k;
      k->foo(12);
    }
    

    beim Aufruf von:

    k->foo(12);
    

    wird aus foo(...), da es nur ein mal im code-Segment vorhanden ist, implizit vom Compiler in ein foo(K &k, int i) konvertiert mit (K &k, int i) als aktuelle Parameterliste, innerhalb der Funktion wird dann aus foo(...) entweder

    foo(K &k, int i, K * const this)
    

    oder

    foo(K * const this, K &k, int i,)
    

    oder liege ich total falsch?



  • Diese Umwandlung passiert nicht direkt in C++, sondern direkt in Assembler. Dort wird zusätzlich zu den Parametern die aktuelle Objektinstanz auf den Stack gepackt.

    Der Parameter 'k' und 'this' in deinem Code sind identisch, daher kannst du es dir einfach als

    foo(K * const this, int i)
    

    vorstellen (bzw. 'const K * const this' bei konstanten Memberfunktionen).



  • Danke für deine Antwort, noch ein paar Fragen dazu:

    Th69 schrieb:

    Der Parameter 'k' und 'this' in deinem Code sind identisch

    Das heisst der Compiler setzt die (konstante) Adresse von k ein.

    Th69 schrieb:

    Dort wird zusätzlich zu den Parametern die aktuelle Objektinstanz auf den Stack gepackt.

    Gib es einen C++ Standard der die Reihenfolge vorschreibt, als erstes auf den Stack oder als Letzter?

    // peudo-code
     push &i
     push &k
    
     oder...
    
     push &k
     push &i
    

    Th69 schrieb:

    (bzw. 'const K * const this' bei konstanten Memberfunktionen).

    Und hier meinst du sicher auf einen konstanten Rückgabewert K, nicht auf eine Elementfunktion die ihre Elemente nicht verändert, oder?



  • DeepCopy schrieb:

    Danke für deine Antwort, noch ein paar Fragen dazu:

    Th69 schrieb:

    Der Parameter 'k' und 'this' in deinem Code sind identisch

    Das heisst der Compiler setzt die (konstante) Adresse von k ein.

    Th69 schrieb:

    Dort wird zusätzlich zu den Parametern die aktuelle Objektinstanz auf den Stack gepackt.

    Gib es einen C++ Standard der die Reihenfolge vorschreibt, als erstes auf den Stack oder als Letzter?

    Nein.

    Th69 schrieb:

    (bzw. 'const K * const this' bei konstanten Memberfunktionen).

    Und hier meinst du sicher auf einen konstanten Rückgabewert K, nicht auf eine Elementfunktion die ihre Elemente nicht verändert, oder?

    Doch. Bei Elementfunktionen, die ihre Elemente nicht verändern, ist this vom Typ const K *const . Die Rückgabe hat damit ja erstmal nichts zu tun.


  • Mod

    pumuckl schrieb:

    Bei Elementfunktionen, die ihre Elemente nicht verändern, ist this vom Typ const K *const .

    Fast. Der Typ von this ist dann const K*
    Da this ein rvalue und somit kein Objekt ist, kann es nicht selbst const-qualifiziert sein.

    Ich frage mich allerdings, wohin die ganzen Fragen eigentlich führen sollen.



  • Mich zu bilden! 😉



  • pumuckl schrieb:

    Zitat:

    Th69 schrieb:
    (bzw. 'const K * const this' bei konstanten Memberfunktionen).

    Und hier meinst du sicher auf einen konstanten Rückgabewert K, nicht auf eine Elementfunktion die ihre Elemente nicht verändert, oder?
    Doch. Bei Elementfunktionen, die ihre Elemente nicht verändern, ist this vom Typ const K *const. Die Rückgabe hat damit ja erstmal nichts zu tun.

    Das glaube ich jetzt so nicht, wo ist der Beweis?

    Ich kenne das:

    // EDIT: verschreiben
     const K *const K::foo(const K * const  i) const;
    

    Und ja es sind 5 const 🙂


  • Mod

    DeepCopy schrieb:

    Und ja es sind 5 const 🙂

    Aber nur 3 davon beeinflussen die Semantik des Funktionsaufrufes, und 4 die der Funktionsdefinition.



  • Pling.... der Großschen ist gefallen

    ein Aufruf von

    this->x = 2;
    

    wäre ja nicht möglich wenn

    K const * const this
    

    wäre, da ich das K Objekt nicht verändern dürfte.

    Merci 😉



  • Ich muss nochmal nachhaken:

    Th69 schrieb:

    Der Parameter 'k' und 'this' in deinem Code sind identisch, daher kannst du es dir einfach als

    foo(K * const this, int i)
    

    vorstellen (bzw. 'const K * const this' bei konstanten Memberfunktionen).

    Ich hab das jetzt mit dem this-Zeiger gut verstanden, aber warum soll ich mir den this-Zeiger jetzt nicht an erster Stelle vorstellen, im Lehrbuch zu C++ Grundlagen (gerade nachgesehen) steht zur ersten Position eigentlich genau dasselbe.

    C++ Lehrbuch schrieb:

    Der Compiler verwandelt

    pt->foo(1.23)
    

    intern in den Aufruf foo(pt, 1.23) um, wobei die Liste der aktuellen Parameter an ihrem linken Ende - also auf Position 1 - um die Adresse des durch den Operanden pt gegebenen Objekts erweitert wird. Diese als Aktualparameter übergebende Objektadresse initialisiert den in der Liste der Formalparamter ebenfalls auf Position 1 befindlichen Parameter this, sodass die Elementfunktion foo darüber anschliessend auf sämtliche Elemente des Objekts *pt zugreifen kann.

    Oder habe ich da was falsch verstanden?



  • DeepCopy schrieb:

    Ich hab das jetzt mit dem this-Zeiger gut verstanden, aber warum soll ich mir den this-Zeiger jetzt nicht an erster Stelle vorstellen,

    Du sollst ihn dir doch an erster Stelle vorstellen:

    Th69 schrieb:

    foo(K * this, int i)
    


  • Wie camper sagte, vielleicht besser:

    foo(K* this, int i)
    

    Und bei const -Memberfunktionen:

    foo(const K* this, int i);
    


  • Nexus schrieb:

    Wie camper sagte, vielleicht besser:

    foo(K* this, int i)
    

    Für mich eher nicht, selbst wenn this ein r_value ist und damit keine Zuweisungen and this möglich sind, so bleibt doch die vom Compiler generierte implizite Konvertierung des des K-Objekts, und die sollte K * const k sein, mit der Ersetzung k == this.

    Ist für mein Verständnis besser, da ich mir so merke was tatsächlich passiert.

    Dank an EUCH ALLE für die gute Hilfe die Ihr mir gegeben habt.
    Gruß DeepCopy



  • camper schrieb:

    pumuckl schrieb:

    Bei Elementfunktionen, die ihre Elemente nicht verändern, ist this vom Typ const K *const .

    Fast. Der Typ von this ist dann const K*
    Da this ein rvalue und somit kein Objekt ist, kann es nicht selbst const-qualifiziert sein.

    Das ist eigentlich beides keine optimale Approximation. Denn bei Dir lässt sich "this" als Variable behandeln, also auch eine neue Adresse zuweisen.

    Der G++ Compiler scheint es übrigens anders zu sehen, was den Typ von this angeht und ob skalare Rvalues const sein dürfen:

    template<typename T> void foo(T&);
    
    class A
    {
    public:
      void bar() { foo(this); }
    };
    

    kompiliert und ruft foo<A*const>(A*const&) auf.

    Demnach wär pumuckl dichter dran.

    Gruß,
    SP


  • Mod

    Sebastian Pizer schrieb:

    Das ist eigentlich beides keine optimale Approximation. Denn bei Dir lässt sich "this" als Variable behandeln, also auch eine neue Adresse zuweisen.

    Dir ist klar, dass kein-Objekt-sein und Variable-sein sich widersprechen?

    Der G++ Compiler scheint es übrigens anders zu sehen, ...

    Dann ist er an dieser Stelle nicht standardkonform, msvc im Übrigen auch nicht.
    Das sagt wie so oft nicht viel aus. Kleines Gegenbeispiel mit Online-Comeau C++

    "ComeauTest.c", line 13: error: no instance of function template "foo" matches the
              argument list
                The argument types that you used are: (A *)
        void bar() { foo(this); }
                     ^
    

    Sebastian Pizer schrieb:

    Demnach wär pumuckl dichter dran.

    Nur ist das hier kein Quiz.

    C++03 schrieb:

    9.3.2 The this pointer [class.this]
    1 In the body of a nonstatic (9.3) member function, the keyword this is a non-lvalue expression whose value is the address of the object for which the function is called. The type of this in a member function of a class X is X*. If the member function is declared const, the type of this is const X*, if the member function is declared volatile, the type of this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile X*.

    C++03 schrieb:

    3.10/9
    Class rvalues can have cv-qualified types; non-class rvalues always have cv-unqualified types.



  • Na das ist doch gut das wir camper haben! 👍

    camper schrieb:

    C++03 schrieb:

    3.10/9
    Class rvalues can have cv-qualified types; non-class rvalues always have cv-unqualified types.

    [OT und vieleicht nicht ganz so schlau von mir]
    Dann heißt cv-qualified types = const volatile - qualified types, richtig? Ich hatte diese Meldung schon und konnte mit dem v in cv-Error des Compilers nicht so richtig was anfangen.



  • @camper: Wir haben uns missverstanden. Es ging in dem Kontext um eine "Emulation" des
    this-Zeigers. Ich dachte, Du meinstest mit

    Der Typ von this ist dann const K*

    dass die Elementfunktion auf low-level Ebene äquivalent zu

    void K__foo(K const* this_, int param);
    

    ist. Das ist natürlich Quatsch, weil this_ hier kein Rvalue ist und verändert werden kann. Der andere Vorschlag von pumuckl war

    void K__foo(K const* const this_, int param);
    

    was zumindest die Eigenschaft, dass this_ nicht veränderbar ist, mit dem Original gemein hat.

    camper schrieb:

    Dir ist klar, dass kein-Objekt-sein und Variable-sein sich widersprechen?

    Ja. Ich habe die zweite Hälfte Deines Absatzes "überlesen", daher das Missverständnis.

    camper schrieb:

    Der G++ Compiler scheint es übrigens anders zu sehen, ...

    Dann ist er an dieser Stelle nicht standardkonform, msvc im Übrigen auch nicht.

    Ich habe nicht behauptet, dass der G++ hier das richtige tut. Ich glaube, der c++ Compiler von IBM ist ein weiterer, der sich nicht daran hält und aus "this" ein const Lvalue macht.

    Gruß,
    SP


Log in to reply