Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt



  • Hallo zusammen,
    Ich bin heute auf folgendes Problem gestoßen und weiß nicht wie ich dabei vorgehen sollte:

    Grundsätzlich will ich in einem Array/Vector verschiedene Instanzen einer Klasse "TMPL" (mit template Parameter T) speichern.
    Die einsetzbaren Template-Klassen sollte dabei alle von Klasse "Base" erben (Im Beispiel: "A", "B")

    Beispiel:

    #include <vector>
    
    struct Base
    {
    	unsigned int ui = 10;
    };
    
    struct A : public Base
    {
    	const char* i = "text";
    	int i = 4;
    	double d = 20.0;
    };
    
    struct B : public Base
    {
    	double d = 20.0;
    	float f = 20.0;
    	long double ld = 23.0L;
    	const char* i = "adfasdfasdf";
    	int i = 4;
    };
    
    template<typename T>
    class TMPL {
    public:
    	T i;
    };
    
    int main() {
    	std::vector<TMPL<Base>> m_vec;
    	m_vec.insert(TMPL<A>()); // HERE VS WON'T COMPILE
    }
    

    Mein Problem ist, dass man Instanzen von unterschiedlichen Versionen der TMPL-Klasse nicht in einem einzelnen Vector speichern kann.

    Hat wer eine Idee, wie ich vorgehen könnte?
    Mfg



  • std::vector<TMPL<Base> *> m_vec;
    

    und wo ist dein Problem?



  • @Swordfish sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    und wo ist dein Problem?

    Und was ist der Sinn dieser Frage, ausser dass sie dem Fragesteller implizit vermittelt, etwas falsches gefragt zu haben haben? Wenn die Lösung für ihn so selbstverständlich wäre, dann hätte er nicht gefragt.

    std::vector<TMPL<Base> *> m_vec;

    Ich vermute, dass hier die Idee sein soll, Pointer auf eine polymorphe Basisklasse in dem Array zu speichern. Das geht zwar, und ist auch oft eine Lösung, im vorliegenden Fall ist allerdings TMPL<Base> keine Basisklasse, sondern die Klasseninstanzen sind völlig unabhängig voneinander, weshalb diese vermeintliche Lösung ebenfalls nicht funktioniert.

    Ohne die TMPL<T>-Klasse ginge das durchaus, z.B. mit einem std::vector<Base*> oder besser so etwas wie std::vector<std::unique_ptr<Base>>, sofern es sich um eine polymorphe Klasse handelt (dazu benötigt Base noch mindestens einen virtuellen Destruktor, also z.B. virtual ~Base() = default;)

    Mit diesem TMPL<T> würde ich aber gerne zunächst fragen wollen, was du damit letztendlich erreichen willst @rnholzinger01 - bevor ich hier Dinge wie std::any, std::variant vorschlage, oder empfehle, die Klassenhierarchie anders zu wählen. Davon hängt nämlich hab, welchen Lösungsansatz man hier am besten wählt oder ob der eingeschlagene Weg überhaupt Sinn macht.

    Finnegan



  • @rnholzinger01 sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Mein Problem ist, dass man Instanzen von unterschiedlichen Versionen der TMPL-Klasse nicht in einem einzelnen Vector speichern kann.

    Hat wer eine Idee, wie ich vorgehen könnte?
    Mfg

    Da die unterschiedlichen Klassen unterschiedlich groß im Sinne von sizeof(T) sind, ist es generell schwierig so etwas durch den Compiler bereit zustellen. Deshalb gibt es keinen direkten Support durch Templates für Dein Vorgehen. Das einzige was direkt geht, ist ein std::shared_ptr<Base> eine Smart Pointer Klasse z.B. std::shared_ptr<Base> oder std::unique_ptr<Base> im Container abzulegen. Man könnte natürlich einen Container bauen, der Platz für die größte von Base abgeleitete Klasse schafft, und es dann ermöglicht direkt im Container die Objekte zu speichern. Aber der Aufwand ist den Nutzen in der Regel nicht wert.



  • @john-0 sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Das einzige was direkt geht, ist ein std::shared_ptr<Base> im Container abzulegen.

    Nein, siehe @Finnegan s Antwort.
    Ein vector mit unique_ptr entspricht auch eher einem "normalen" vector (dem gehört sein Inhalt auch und shared nichts).



  • @Jockelx sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Nein, siehe @Finnegan s Antwort.

    Ja, erwischt schlecht formuliert. „Man muss dann eine der Smart Pointer Klassen von C++ nutzen.“, wäre mit Sicherheit die bessere Wortwahl gewesen.

    Was das Thema unique_ptr betrifft, man kann keine Kopien erzeugen, was von der Logik an Brilianz für den Entwurf eindeutig nicht zu überbieten ist. Nur hat C++ nicht an Ausdruckskraft durch alle die notwendigen Sprachänderungen gewonnen, die dafür notwendig waren.



  • @john-0 sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    @Jockelx sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Nein, siehe @Finnegan s Antwort.

    Ja, erwischt schlecht formuliert. „Man muss dann eine der Smart Pointer Klassen von C++ nutzen.“, wäre mit Sicherheit die bessere Wortwahl gewesen.

    Ne, darum geht´s nicht. Der Punkt ist, dass TMPL<Base> ein anderer Typ als TMPL<A> ist, auch wenn A von Base erbt.



  • @john-0 sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Was das Thema unique_ptr betrifft, man kann keine Kopien erzeugen, was von der Logik an Brilianz für den Entwurf eindeutig nicht zu überbieten ist. Nur hat C++ nicht an Ausdruckskraft durch alle die notwendigen Sprachänderungen gewonnen, die dafür notwendig waren.

    Naja, das Problem hast du aber in C#, Java oder sowas auch.



  • @john-0 sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Was das Thema unique_ptr betrifft, man kann keine Kopien erzeugen, was von der Logik an Brilianz für den Entwurf eindeutig nicht zu überbieten ist.

    Man kann durchaus Kopien erzeugen:

    std::unique_ptr<int> p = std::make_unique<int>(5);
    int* zeiger_kopie = p.get();
    std::unique_ptr<int> objekt_kopie = std::make_unique<int>(*p);
    

    Was allerdings zurecht schwer gemacht wird, ist zwei besitzende Zeiger (unique_ptr) zu erzeugen, die sich um dasselbe Objekt zanken.

    Das wirkt sich dann natürlich auch auf Container aus, die solche unique_ptr halten. Einen std::vector<std::unique_ptr<int>> müsste man schon manuell in einen std::vector<int*> oder einen anderen Vektor des selben Typs kopieren. Das braucht man meiner Meinung nach aber nur recht selten, und wenn doch, dann halte ich die Wahrscheinlichkeit für recht hoch, dass dann auch ein Vektor mit shared_ptr gerechtfertigt ist, bei dem das Kopieren kein Problem ist.



  • @rnholzinger01 sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Mein Problem ist, dass man Instanzen von unterschiedlichen Versionen der TMPL-Klasse nicht in einem einzelnen Vector speichern kann.

    Naja du kannst immer einen shared_ptr<void> nehmen. Damit kannst du alles in den selben vector stopfen, ganz egal um welche Klasse es sich handelt. Oder du kannst TMPL von einer non-template Basisklasse mit virtuellem Destruktor ableiten und dann unique_ptr<DieseNonTemplateBasisklasse> in den vector stecken.

    Oder, falls du nur Zugriff auf Base Funktionen brauchst, kannst du einen shared_ptr<Base> nehmen. Das funktioniert auch wenn das Objekt tatsächlich ein TMPL<A> ist welches nur ein A (und damit ein Base) beinhaltet, aber nicht davon erbt. Dazu gibt es den aliasing-Konstruktor. So kannst du einen shared_ptr<Base> bekommen der auf das in TMPL<A> enthaltene A Objekt zeigt, gleichzeitig aber das ganze TMPL<A> am Leben hält.

    Ansonsten müsste man wissen was du mit den TMPL<T> Instanzen in dem vector machen möchtest.



  • @Finnegan sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Man kann durchaus Kopien erzeugen:

    Er meinte sicherlich, dass das hier mit den Basiszeigern aber nicht so einfach geht. Oder wie kopierst du da die Objekte (=rufst den richtigen Copy-ctor auf)?
    Die Möglichkeit die ich kenne, ist halt eine "clone"-Funktion zu basteln (daher auch der Verweis auf C# & java).



  • @Jockelx sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Die Möglichkeit die ich kenne, ist halt eine "clone"-Funktion zu basteln (daher auch der Verweis auf C# & java).

    Du bist so dermaßen eklig!



  • @Swordfish sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Du bist so dermaßen eklig!

    Hör auf mit deinen Beleidigungen, das nervt. Schlag lieber eine Alternative vor. Ich verstehe dich nicht. Ein Teil deiner Beiträge ist sachbezogen und gut, der andere Teil irgendwie störend, merkwürdig und nutzlos.



  • @wob sagte in Array/Vector einer Template-Klasse, deren Template-Typ von einer Base Klasse erbt:

    Schlag lieber eine Alternative vor.

    Das war keine Beleidigung, sondern eine Feststellung. Beobachtbares Verhalten. Aber na schön:

    #include <memory>
    #include <vector>
    #include <iostream>
    
    struct Base {
    	virtual void talk() = 0;
    	virtual ~Base() {}; };
    
    struct A : Base {
    	virtual void talk() override { std::cout << "A\n"; }
    	virtual ~A() { std::cout << "A::d-tor()\n"; }
    };
    
    struct B : Base {
    	virtual void talk() override { std::cout << "B\n"; }
    	virtual ~B() { std::cout << "B::d-tor()\n"; }
    };
    
    struct C : Base {
    	virtual void talk() override { std::cout << "C\n"; }
    	virtual ~C() { std::cout << "C::d-tor()\n"; }
    };
    
    int main()
    {
    	std::vector<std::shared_ptr<Base>> foo;
    	foo.push_back(std::make_shared<A>());
    	foo.push_back(std::make_shared<B>());
    	foo.push_back(std::make_shared<C>());
    
    	for (auto const &f : foo)
    		f->talk();
    }
    

    Output:

    A
    B
    C
    A::d-tor()
    B::d-tor()
    C::d-tor()
    

    Aber ich weiß nicht, wieso ich hier das Kindermädchen spielen soll. @hustbaer hat schon alles gesagt.

    ach so. kopieren. warum will ich das?

    	Base *bar{ foo[1].get() };
    	bar->talk();
    

    *Wirklich* kopieren kann /* edit: */ wenn man den Typ nicht kennt /* edit */ in einer stark typisierten Sprache nur über verrenkungen gehen.


Anmelden zum Antworten