Slicing



  • Hallo,

    gehe ich recht in der Annahme, dass hier slicing stattfindet? Auch wenn beide Klassen die gleiche Größe haben und Derived nur Typinformationen enthält? Erwartet mich hier UB?

    struct Base
    {
       void* m1;
       void* m2;
    
       Base() : 
          m1( nullptr ),
          m2( nullptr )
       {
       }
    
       // Edit: Fehlenden Konstruktor ergänzt
       Base( void* p1, void* p2 ) :
          m1( p1 ),
          m2( p2 )
       {
       }
       // Ende Edit
    
       virtual ~Base()
       {
       }
    };
    
    template<typename T1, typename T2>
    struct Derived : Base
    {
       Derived( void* p1, void* p2) :
         Base( p1,p2 )
       {
       }
    };
    
    template<typename T1, typename T2>
    Derived<T1,T2> make_obj( T1* p1, T2* p2 )
    {
       return Derived<T1,T2>( p1,p2 );
    }
    
    int main()
    {
       int i1 = 0;
       int i2 = 1;
    
       // ungültig oder nicht?
       Base b = make_obj( &i1, &i2 );
    }
    


  • Wenn die Basisklasse und die abgeleitete Klasse gleich gross sind, dann kann per Definition kein Slicing auftreten.



  • turbo autist schrieb:

    Wenn die Basisklasse und die abgeleitete Klasse gleich gross sind, dann kann per Definition kein Slicing auftreten.

    Pauschal ist das so schon mal falsch, die vtable ist vom slicing betroffen.

    #include <iostream>
    
    using namespace std;
    
    struct Base
    {
       Base()
       {
       }
    
       virtual ~Base()
       {
       }
    
       virtual void f()
       {
          cout << "Base" << endl;   
       }
    };
    
    struct Derived : Base
    {
       Derived()
       {
       }
    
       virtual void f()
       {
          cout << "Derived" << endl;   
       }
    };
    
    int main()
    {
       Base b = Derived();
       b.f();
    }
    


  • DocShoe schrieb:

    Hallo,

    gehe ich recht in der Annahme, dass hier slicing stattfindet? Auch wenn beide Klassen die gleiche Größe haben und Derived nur Typinformationen enthält? Erwartet mich hier UB?

    ...
       // ungültig oder nicht?
       Base b = make_obj( &i1, &i2 );
    }
    

    Naja Base hat keinen ctor mit zwei Parametern. Du versuchst aber einen solchen aufzurufen. Das geht natürlich nicht. Ich nehme aber mal an das war ein Versehen und nicht Teil der Frage die du eigentlich stellen wolltest.

    Und abgesehen davon...
    Nein, da ist nix UB oder ungültig. Wieso sollte es auch sein? Slicing ist ja aus Sicht des Standards ne ganz normale, "wohldefinierte" Sache.



  • DocShoe schrieb:

    gehe ich recht in der Annahme, dass hier slicing stattfindet? Auch wenn beide Klassen die gleiche Größe haben und Derived nur Typinformationen enthält? Erwartet mich hier UB?

    Ob es Slicing ist, kommt drauf an, wie man den Begriff genau definiert. Bei Dir wird jedenfalls der Kopierkonstruktor von Base mit einem temporären Derived-Objekt als Parameter benutzt, um b zu erzeugen. Der Kopierkonstruktor wird hier vom Compiler automatisch generiert. Er kopiert m1 und m2 . Der statische und dynamische Typ des Ausdrucks b ist dann Base und nicht Derived .

    Das ist schon gültig und kein undefiniertes Verhalten. Die Frage ist nur, ob der Effekt der ist, den Du willst. Ich würde mal drauf tippen, dass Du das Verhalten nicht magst und Du lieber Derived als dynamischen Typen haben willst. Falls dem so ist, schreib doch einfach

    auto b = make_obj( &i1, &i2 );         // statisch: Derived, dynamisch: Derived
    
    // oder                                // (virtual ist hier wichtig)
    // Base&& b = make_obj( &i1, &i2 );    // statisch: Base,    dynamisch: Derived
    

    Dann liegt ein Derived-Objekt im automatischen Speicher und der Ausdruck b hat den dynamischen Typen Derived . Der Destruktor, der aufgerufen wird, ist dann Derived::~Derived .



  • hustbaer schrieb:

    DocShoe schrieb:

    Hallo,

    gehe ich recht in der Annahme, dass hier slicing stattfindet? Auch wenn beide Klassen die gleiche Größe haben und Derived nur Typinformationen enthält? Erwartet mich hier UB?

    ...
       // ungültig oder nicht?
       Base b = make_obj( &i1, &i2 );
    }
    

    Naja Base hat keinen ctor mit zwei Parametern. Du versuchst aber einen solchen aufzurufen.
    ...

    Ist das tatsächlich so? Es wird doch der Derived-Konstruktor aufgerufen (in make_obj).



  • Aber in Zeile 21 im Code von DocShoe braucht er dann einen Base-Konstruktor mit 2 Parametern:

    Derived( void* p1, void* p2) :
         Base( p1,p2 )
       {
    

    Edit: DocShoe und Hi verwechselt.



  • Danke für die Antworten, die Motivation ist eine Klasse zu definieren, dass sich zB in einen Vektor stecken lässt, auf dem Stack erzeugt werden kann und sich einzelne Objekte trotzdem unterschiedlich verhalten. Das Ganze hat mit mit meinen Versuchen zu tun, einen Ersatz für das borlandspezifische __closure zu finden (was im Grunde ziemlich komfortable pointer-to-member-functions sind). Ich habe versucht, etwas ohne offensichtliche templates zu basteln, die beiden void* Zeiger in base sind Zeiger auf Objekte und member-funktionen bzw. Zeiger auf freie Funktionen, die auf statisch Template Funktionen in der abgeleiteten Klasse zeigen.
    Habe den Versuch allerdings beerdigt, da haben schlauere Leute als ich viel mehr Gehirnschmalz investiert. Inzwischen bin ich bei boost::function angekommen und muss mich nur noch davon überzeugen lassen, dass unser C++ Compiler das auch ordentlich kann. Sah am Anfang nicht so aus, aber letzte Tests waren erfolgreich.

    Edit:
    Ja, der o.g. Code ist nicht lauffähig, es fehlt der Base Konstruktor mit zwei Aufrufparametern.


Log in to reply