Aufruf von Funktion der abgeleiteten Klasse in Template-Funktion



  • Danke euch beiden!

    Leider habe ich die beschriebene Möglichkeit von Nexus (noch nicht) verstanden.

    Redest du von einem zusätzlichen Template-Parameter für die Klasse oder Funktion?
    Aber bräuchte ich dann in der Basisklasse nicht trotzdem eine virtuelle Funktion?

    Gruß,
    MaPoX



  • Dein Problem ist ja grundsätzlich, dass bar() erst in der abgeleiteten Klasse bekannt ist, daher musst du die abgeleitete Klasse über einen Templateparameter bekannt machen.

    class Base
    {
        public:
            template <typename D, typename T>
            void foo(T* t)
            {
                static_cast<D*>(this)->bar(t);
            }
    };
    
    class Derived : public Base
    {
        public:
            void bar(int* t);
    };
    
    int main()
    {
        int x;
        Derived d;
        d.foo<Derived>(&x);
    }
    

    Die letzte Zeile könntest du dann in eine freie Funktion packen, um D automatisch herleiten zu lassen.

    Allerdings sehe ich nicht gerade, was dir diese Herangehensweise bringt. Denn so hast du keine Abstraktion, da du die abgeleiteten Klassen kennen musst, und prinzipiell könntest du Derived::bar() direkt aufrufen. Andererseits möchtest du keine dynamische Polymorphie, da du dich nicht auf eine Schnittstelle festlegen kannst...

    Was willst du erreichen? Kannst du das ohne bar , foo und Konsorten beschreiben?



  • Ich glaube, es gibt hier ein paar Lösungen, die sind aber nicht sonderlich hübsch. Wenn ich mir die hypothetischen Lösungen so anschaue, finde ich, dass das Gesamtbild nicht sauber ist und würde wohl alles etwas umbauen. Kannst Du, wie bereits gefragt, ein besseres Gesamtbild vermitteln, wozu Du das brauchst?

    Besagten Lösungen kommen nicht mit statischer Polymorphie alleine aus, dafür fehlt einfach Template-Vererbung in C++. Kombiniert mit Laufzeit-Polymorphie gehen Dinge wie so was:

    class ObjBase
    {
    protected:
    // evtl. Zugriffsmethoden für Derived
    
    public:
    	virtual void bla() = 0;
    };
    
    class Obj : public ObjBase
    {
    public:
    	void bla() {std::cout << "Hi! I'm Obj.";}
    };
    
    class Base
    {
    public:
    	template<typename T> void foo(T* t) {bar(t);}
    
    	virtual void bar(ObjBase*) = 0;	
    };
    
    class Derived : public Base
    {
    public:
    	void bar(ObjBase* t) {t->DoWhatEverThisBarShouldDo(this);}
    
    	friend class ObjBase;
    };
    
    int main()
    {
    	Derived x;
    	x.foo(new Obj);
    	return 0;
    }
    

    Klar, dass man hier Abstriche machen muss. Mein friend-Gedöns dient dazu, falls alle von ObjBase geerbten Klassen an Derived-Objekten etwas ändern sollen, führt allerdings wiederum dazu, dass man den this-Zeiger von Derived übergeben müsste (nicht implementiert). Ich glaube, das geht in die Richtung von das, was man Double-Dispatching nennt, oder?

    Jedenfalls betreten wir einen komplizierten Bereich, der nicht 100%ig elegant lösbar zu sein scheint. Um dennoch die beste Lösung zu finden oder sogar diesen Bereich gar nicht betrete zu müssen, brauchen wir mehr Informationen.



  • Nexus schrieb:

    class Base
    {
        public:
            template <typename D, typename T>
            void foo(T* t)
            {
                static_cast<D*>(this)->bar(t);
            }
    };
    
    class Derived : public Base
    {
        public:
            void bar(int* t);
    };
    

    Oder sö

    template<typename D> class Base
    {
        public:
            template <typename T>
            void foo(T* t)
            {
                static_cast<D*>(this)->bar(t);
            }
    };
    
    class Derived : public Base<Derived>
    {
        public:
            void bar(int* t);
    };
    

    http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern



  • brotbernd schrieb:

    Oder sö

    Sö wollte er es ja nicht 😉

    MaPoX schrieb:

    Deswegen kann ich den Template-Parameter auch nicht an die Klasse binden.



  • Ich glaube da ging es ihm um den Typ T, da er die Methode bar für verschiedene Typen überladen möchte.



  • Danke für die zahlreichen Antworten!

    Ich glaub, ich wollte einfach zu sehr Code reduzieren 🤡



  • Das reicht mir nicht. Jetzt will ich genau wissen, was Du vorhattest und wieso Du diese Lösung in Betracht gezogen hast.



  • Eisflamme schrieb:

    Das reicht mir nicht. Jetzt will ich genau wissen, was Du vorhattest und wieso Du diese Lösung in Betracht gezogen hast.

    Okay 🙄

    Ich dachte an etwas wie Folgendes, wobei die Einträge in der passenden map landen sollen. Warum ich diese Lösung verwenden wollte... Ich finde sie bietet sich an:

    #include <iostream>
    #include <map>
    
    class RegistrationException{};
    class LicensePlate{};
    class BarCode{};
    
    class Car{
    public:
    	typedef LicensePlate identification_type;
    };
    class MotorBike{
    public:
    	typedef LicensePlate identification_type;
    };
    class Beer{
    public:
    	typedef BarCode identification_type;
    };
    class IceCream{
    public:
    	typedef BarCode identification_type;
    };
    
    class RegistrationService{
    public:
    	template<typename T>
    	typename T::identification_type& registerObject(T* t){
    		typename T::identification_type* i = getIdentification(*t);
    		if(i){
    			throw RegistrationException(/*"Already registred"*/);
    		}
    		i = createIdentification(*t);
    		return *i;
    	}
    };
    
    class VehicleRegistrationService : public RegistrationService{
    public:
    	LicensePlate* generateLicensePlate();
    	LicensePlate* getIdentification(Car& car);
    	LicensePlate* createIdentification(Car& car);
    	LicensePlate* getIdentification(MotorBike& bike);
    	LicensePlate* createIdentification(MotorBike& bike);	
    
    private:
    	std::map<Car*, LicensePlate*> cars;
    	std::map<MotorBike*, LicensePlate*> bikes;
    };
    
    class ProductRegistrationService : public RegistrationService{
    public:
    	BarCode* generateBarCode();
    	BarCode* getIdentification(Beer& beer);
    	BarCode* createIdentification(Beer& beer);
    	BarCode* getIdentification(IceCream& iceCream);
    	BarCode* createIdentification(IceCream& iceCream);	
    
    private:
    	std::map<Beer*, BarCode*> cars;
    	std::map<IceCream*, BarCode*> bikes;
    
    };
    
    int main(){
    	VehicleRegistrationService vehicles;
    	vehicles.registerObject(new Car(/*SeppJs Alpha Romeo*/));
    	vehicles.registerObject(new MotorBike(/* Nexus' Harley Davidson*/));
    	ProductRegistrationService products;
    	products.registerObject(new Beer(/* Veltins Pilsener*/));
    	products.registerObject(new IceCream(/* Peters Chocopolitan */));
    }
    

    EDIT: In diesem Fall sind es (zufällig gleichviele) zwei Typen pro RegistrationService



  • Hi,

    Wieso nicht so?

    class Vehicle
    {
    public:
    	typedef LicensePlate Identifier;
    };
    
    class Car : public Vehicle
    {
    };
    
    class MotorCycle : public Vehicle
    {
    };
    
    template<typename RegisteredObject>
    class RegistrationService
    {
    private:
    	typedef typename RegisteredObject::Identifier Identifier;
    	typedef std::map<Identifier, RegisteredObject> Registration;
    public:
    	void registerIt(const Identifier& key, const RegisteredObject& obj); // register ist (quasi-obsoletes) Schlüsselwort -.-
    };
    
    int main()
    {
    	typedef RegistrationService<Vehicle> VehicleRegistrationService;
    
    	VehicleRegistrationService bigGarage;
    	bigGarage.registerIt("HH AB-123", Car());
    
    	return 0;
    }
    

    Sind noch Fehler und Slicing und so drin, aber die Idee versteht man ja, hoffe ich. -.-



  • Ich schätze mal Car und MotorCycle sollten von Vehicle erben(?)

    Weil ich an anderer Stelle über alle Autos bzw. Motorräder drüber iterieren möchte und Eigenschaften abfrage, die nicht zwangsläufig beide Fahrzeuge haben.

    Eisflamme schrieb:

    // register ist (quasi-obsoletes) Schlüsselwort -.-
    

    Ist mir auch aufgefallen...
    Wofür?



  • Ja, sorry, ich mach immer so Schludercode, wenn ich den nicht teste.

    Hab's korrigiert. So, wie es jetzt ist, ist ja auch noch Slicing drin, was sicherlich unerwünscht ist, daher ist eine Zeigervariante des Containers sicherlich besser. 🙂

    Wenn Du übrigens drüberiterieren willst und Eigenschaften abfragen möchtest, die nicht beide haben, brauchst Du ja Downcasts, die aber auch zu hinterfragen sind, weil man an der Stelle, wo man nur die Basisklasse speichert, eigentlich auch nur diese benötigen sollte. Daher frage ich Mal direkt weiter, wofür Du das brauchst. 😉 Aber erst, wenn das hier geklärt ist.

    Ach und register hatte früher Mal die Aufgabe, dass man direkt den Registerspeicher der CPU nutzen konnte, was natürlich Performanceverbesserungen gebracht hat.

    Heute ist aber Mr. Compiler schlau genug, dass er es selbst machen kann (wahrscheinlich sogar besser). Das Schlüsselwort wird afaik sogar ignoriert, wenn der Compiler es sinnlos findet. Daher braucht man es wirklich gar nicht mehr verwenden.

    Edit:
    Ich sehe gerade, Du wolltest mehrere Maps pro Subklasse haben. Aber wieso benötigst Du die beiden maps innerhalb einer Klasse? Ich glaube, Du musst noch etwas weiter ausholen.


Anmelden zum Antworten