Virtual template...



  • Hi,
    ich hab ein kleines Problem mit virtuals und templates, was ja nicht so gut klappt 🙂
    Meine Struktur sieht ungefähr so aus:

    // Diese Klasse erfordert Templates, weil eine Variable unbedingt generisch sein muss
    template <class T>
    class TClass{
    
      T var;
    }
    

    Nun habe ich eine andere Klasse, welche eigentlich nur vererbt werden soll und bestimmte Methodenköpfe beinhaltet, die die ebende Klasse unbedingt implementieren muss. Eine der Methode hat aber diese Signatur:

    class AbstracClass{
    
      template<class T>
      void use(const TClass<T> var);
    }
    

    Der Haken ist, diese use Methode müsste eigentlich virtual sein und eigentlich auch pure virtual, also abstrakt, weil es keinen Sinn macht, diese Methode in AbstracClass zu definieren.

    Bei virtual void use (..) = 0; meckert ja der Compiler (natürlich auch bei nur virtual).

    Leider komme ich so nicht weiter, was kann ich tun? Ich kann nicht auf das Template in TClass verzichten, brauche aber wegen Polymorphie "use" mindestens als virtual (besser pure virtual) 😞

    Gibt es da einen Weg ohne auf Template zu verzichten? Jedesmal wenn die TClass instanziert wird ist T var vom anderen Typ, also hoch variabel. Jedoch funktioniert das ganze Programm auf der Basis dass man in einen AbstractClass Pointer eine konkrete Klasse packt, jedoch immer mit dem AbstractClass pointer arbeitet 😕



  • Du koenntest eine Basisklasse ohne Template einfuehren.

    class TAbstractClass
    {
    public:
      virtual ~TAbstractClass() = 0
      {
      }
    };
    
    template <class T>
    class TClass: public TAbstractClass
    {
      T var;
    };
    
    class AnotherAbstracClass
    {
      void use(TAbstractClass const&);
    };
    

    Musst dann in TAbstractClass das Zeug hauen, was bei use() benoetigt wird.

    Gruss,
    DeSoVoDaMu



  • Das sieht ein wenig schwierig aus, wie ich zugeben muss.

    Ich muss ja noch an die Methoden der TClass ran kommen, vereinfacht sind das Getter und Setter der T var. Die können somit ja schlecht in die templatelose Basisklasse rein.
    Die Methoden brauch ich aber dennoch und wenn ich einen Pointer der Oberklasse habe, kann ich die Methoden der Template Unterklasse ja nicht nutzen



  • Um diese Uhrzeit fällt mir nur das ein:

    Verzichte auf template in Abstract-Class und überlade für deine Typen:

    class AbstractClass
    {
      public:
             virtual void use(const TClass<int>& var) = 0;
             virtual void use(const TClass<Irgendwas>& var) = 0;
    };
    

    Irgendwo hatte ich das Problem schonmal gesehen/gehabt. Vllt fällt mir morgen ne schönere Lösung ein 😉



  • Und was spricht gegen:

    template<class T> 
    class AbstracClass
    { 
      virtual void use(const TClass<T> var) = 0;
    };
    

    Du kannst ja nicht eine Klasse definieren, die gleichzeitig mit beliebigen Datentypen hantieren kann.
    Ansonsten geht wirklich nur die Lösung von KasF, d.h. die Methoden für jeden benötigten Datentyp überladen.



  • Es geht so leider nicht, weil diese Klasse, mit der Methode use kein Template sein kann, eine Instanz einer Unterklasse dieser Klasse wird nur einmal erzeugt, use wird aber oft mit verschiedenen Parametern aufgerufen.



  • Worüber genau beschwert sich denn der Compiler?

    Allgemein: 'virtual' bedeutet, daß zur Laufzeit entschieden wird, welche Methode eigentlich aufgerufen wird - 'template<>' bedeutet, daß die Methode zur Compilezeit aufgerufen wird. Das liese sich theoretisch kombinieren, weil sich beides auf einen anderen Teil des Methodenaufrufs "a.use(b)" bezieht, allerdings sprengt sowas vermutlich die Grenzen des Compilers - schließlich muß er beim Compilieren erkennen, welche Instanzen der use()-Methode er benötigt - und das, obwohl die derived-Klasse und die Instanziierung nicht einmal in der selben Quelldatei erwähnt werden.

    Also: Was willst du erreichen?



  • Pointer von der AbstractClass soll eine konkrete Klasse habe und eine Methode use nutzen können, die eine generische Var nimmt.
    use soll aber in der konrekten Klasse überladen werden. Das use des AbstractClass Pointer ruft aber nicht das das use der konkreten Klasse auf, weil es nich virtual ist



  • Da hast du das Problem, daß der Compiler für die konkrete Klasse wissen muß, welche Instanziierungen der Methode er tatsächlich braucht (nochmal: worüber genau beschwert sich der Compiler?).

    Aber gehen wir einen Schritt zurück und überdenken den Weg, der zu dieser Konstellation geführt hat: Was hattest du ursprünglich vor?



  • 'void AbstractClass:use(TClass<T> & var)': Vorlagen der Memberfunktion können nicht virtuell sein

    Ich hab eine Klasse "Familie", die is Abstrakt, diese Familie hat eine Methode use, aber bei der Klasse Familie macht dies keinen Sinn, weil Familie use nicht mit inhalt füllen kann. Use ist pure virtual, weil das Programm ein Array voller Familie Objekte hat, Vater, Mutter usw und packt die in einen Familie Pointer und ruft use() auf, dies Klasse, weil es virtual ist.

    Nun muss ich use aber mit einem Parameter aufrufen der variiert. Ich habe eine Container Klasse hier TClass, die hat eine T var und ein bisschen drum herum, also zusätzlichen Methoden und Attribute.
    Das eigentliche Ziel ist es einem Familien Pointer mit use diese TClass instanz zu übergeben, so dass die jeweiligen Implementationen diese verarbeiten.
    (Wie diese Klasse erkennen ob T int, string usw ist, ist irrelevant, dass klappt und wird auch alles gebraucht)
    So wie es aussieht, muss ich eventuell T in String ändern und falls man einen Int braucht, dann nutzt man halt atoi oder so, ist sehr schlecht, wie ich finde.



  • Da steht der Compiler genau vor dem Problem, das ich oben schon erwähnt habe: er hat die Instanziierung (Aufruf) und Implmenentierung (Methodenrumpf) nicht an einer Stelle - und im Gegensatz zu "normalen" Templates auch keine Möglichkeit, beides miteinander in Verbindung zu setzen. Da bleibt dir wohl nichts anderes übrig als dich auch einige Grundtypen einzuschränken und deine use()-Methode für diese Typen zu überschreiben.

    (was für Daten willst du eigentlich an use() übergeben?)



  • Sind verschiedene Objekte, int[], string[], floats[],das gleiche auch ohne Arrays eventuel, das wird wohl alles sein.
    Muss gucken, ob ich überladungen machen, aber mit Überladungen hab ich das Problem, dass ich use 3 mal implementieren muss, wobei use ne Menge arbeit machen soll, eigentlich nahe zu die ganze Arbeit und da kann ich auch nicht so gut den Inhalt verpacken, in eine dritte Methode, weil ich ja den Wert noch brauche und der kann mal wieder int[], string[] oder float[] sein 😃



  • Evtl. könntest du auch die Klasse TClass<T> von einer gemeinsamen Basisklasse erben lassen und diese dann bei use angeben:

    class TBaseClass
    {
    
    };
    
    template<typename T>
    class TClass : public TBaseClass
    {
    
    };
    
    class AbstracClass
    { 
      virtual void use(const TBaseClass &var) = 0;
    };
    

    Dies funktioniert natürlich nur, wenn innerhalb der Methode use() nicht explizit auf die Template-Variable T zugegriffen wird.

    Am besten du zeigst mal, wie deine Beispiel use-Methode aussehen soll?

    Wie viele verschiedene (konkrete) Datentypen hast du denn (evtl. reicht ja std::string und int)?



  • Und was willst du mit diesen Typen jeweils machen? (alleinstehende int und float sind sich ähnlich genug, um notfalls von einer Funktion geschluckt zu werden; Array-Verarbeitung kannst du in einer nichtvirtuellen Template-Funktion weiterdelegieren - bleiben nur noch zwei virtuelle Funktionen für Zahlen und Strings:

    virtual void use(const string& data) = 0;
    virtual void use(double data) = 0;
    template<typename T>
    void use(T* array,size_t len)
    {
      for(int i=0;i<len;++i) use(array[i]);
    }
    


  • Seikilos schrieb:

    ...

    Ich tippe eher auf ein falsches Design. Was soll das eigentlich bringen?
    Entweder du willst Polymorph arbeiten, dann müssen deine Objekte die gleiche Schnittstelle haben oder du greifst eh auf die konkrete Schnittstelle zu, dann brauchst du aber auch nicht solche Klimmzüge.

    Wenn du den Unterobjekten allesamt gänzlich andere Parameter übergeben willst, muss ich davon ausgehen das zum Zeitpunkt der Übergabe dein konkretes Objekt bekannt ist, sonst macht die Übergabe ja eh keinen Sinn. Vielleicht ist in deinen Fall ein anderes Konzept besser geeignet...

    Aber ohne ein konkreteres Beispiel könnte ich nur grob ins blaue raten (ja, ich habe einen Verdacht, aber nein, ohne das ich mir sicher bin schreibe ich hier jetzt keinen Roman hin).

    cu André



  • Was meinst du mit nicht explizit auf T zugreifen? Ich brauch schon in use konkret den inhalt, wenns ein T var[] wäre, hol ich mir die Werte dann zb nach int = var[0], meinst du dies mit auf T zugreifen?

    Zu dem Rest, in use soll sich anhand der Klasse des T Typen entscheiden ob es Int oder String ist, oder sonst was und abhängig von internen zuständen werden dann andere Methoden aufgerufen.

    Explizit gibts use noch nicht, nur in der Theorie, macht ja kein sinn das zu implementieren, wenn es eh nicht geht 🙂

    Überladung wäre schlecht, weil ich dann mal hier mal da Methoden aufrufe habe, das ist so, als würd ich ein großes switch in mehrere Methoden auf teilen (auch wenn switch hier nicht geht 🙂 )



  • Seikilos schrieb:

    Überladung wäre schlecht, weil ich dann mal hier mal da Methoden aufrufe habe, das ist so, als würd ich ein großes switch in mehrere Methoden auf teilen (auch wenn switch hier nicht geht 🙂 )

    Wieso wäre das schlecht - der Compiler wählt doch je nach Bedarf genau die Methode auf, die er in der aktuellen Situation benötigt.



  • @asc, ich hab einen Event generator, wenn etwas passiert, soll die ganze Familie davon erfahren, der Event generator könnte Zahlen[] generieren, oder nur einen String, alles mögliche an Events. N beispiel was mir einfallen würde, ist wenn der Event generator ein Eingabefeld ist und abhängig was man dort eingibt, wird dies an die Familie gesendet, die muss dann aber damit klarkommen.

    Edit: CStoll, naja int, int[] usw und ich hab 6 Überladungen, und diese Switch ding ist eine sinngruppe, aufgeteilt auf irrelevante dinge wie mal int mal float bröselt das Layout auseinander 🙂



  • Seikilos schrieb:

    @asc, ich hab einen Event generator, wenn etwas passiert, soll die ganze Familie davon erfahren, der Event generator könnte Zahlen[] generieren, oder nur einen String, alles mögliche an Events. N beispiel was mir einfallen würde, ist wenn der Event generator ein Eingabefeld ist und abhängig was man dort eingibt, wird dies an die Familie gesendet, die muss dann aber damit klarkommen.

    Und wie sehen die Reaktionen eines Familienmitglieds auf einen String oder ein int-Array typischerweise aus? Da dürfte es schon schwierig werden, das in einen einheitlichen Code-Block (aka Template) zu pressen.



  • Die TClass transportiert mehr als nur die T var, auch ein paar Indikatoren, enum, int wert irgendwas, an dem man unterscheiden kann, was es für ein Wert in der T var erwarten darf und dem gemäß packt man den Inhalt in ein int, string oder sonstiges und ruft evtl eine Methode mit dem int, mit dem String oder float als Parameter auf, oder tut nichts, weil der Wert nicht zu gebrauchen ist usw.


Anmelden zum Antworten