template Parameter einschrenken



  • Hi zusammen.. das wird wohl für die meisten von euch eine ganz einfache Frage ein aber ja.. ich bin jetzt schon ne weile eine Antwort darauf am suchen also.. versuch ichs jetzt mal hier:

    Üblicherweise wir eine template klasse etwa so definiert:

    template <class T>
    class Test
    {
    ...
    }

    Was ich gesehen habe ist, dass man statt class z.B. int oder fload oder als Typen-Parameter verwenden kann. Und ich habe auch gelesen,dass es möglich sein sollte, dass man z.B. nur Subklassen einer bestimmten Basisklasse als Parameter mitgeben kann, nur hab ich nicht herausgefunden, wie man das mach..!?

    Konkret interessieren mich folgende Fälle:

    - Nur Subklassen einer bstimmten Basisklasse sind erlaubt (und die Basisklasse selbst natürlich)
    - Nur Zeiger auf bestimmte Basisklasse und deren Subklassen sind erlaub

    Hat jemand eine Idee, wie man das machen könnte..?

    Merci und Grüsse, Dominic



  • bayo schrieb:

    Konkret interessieren mich folgende Fälle:
    - Nur Subklassen einer bstimmten Basisklasse sind erlaubt (und die Basisklasse selbst natürlich)

    Boost bietet ein STATIC_ASSERT und ein template Is_Derived oder so ähnlich, mit beidem zusammen kannst du dafür sorgen, dass die Compilierung deines Codes mit allen anderen Klassen als Parameter fehlschlägt. Die Bibliothek Loki, die von Alexandrescu in seinem Modern C++ Design vorgestellt wird, ist auch frei erhältlich und enthält ähnliche features.
    Wenn es dir nur darum geht, dass die Klassen die du verwendest das Interface deiner Basisklasse implementieren müssen, dann reicht es aber, dieses Interface in deinem Template einfach zu verwenden. Zwar können dann auch Klassen verwendet werden, die das Interface deiner Basisklasse implementieren, obwohl sie nicht von ihr abgeleitet wurden, aber das ist oft sogar erwünscht. Das ganze nennt sich dann Compilezeit Polymorphie.

    - Nur Zeiger auf bestimmte Basisklasse und deren Subklassen sind erlaub

    Entweder du dereferenzierst den Parameter in deinem Template einfach und benutzt dann wie oben beschrieben einfach das Interface deiner Basisklasse. Oder du sorgst via static-assert &Co. dafür, dass das Ergebnis einer Dereferenzierung auch wirklich von deiner basisklasse abgeleitet ist. In beiden Fällen kann man pointer, smart pointer und alle anderen Klassen verwenden, die den Dereferenzierungsoperator überladen haben und als Ergebnis ein Objekt deiner Basisklasse (oder eines ihrer Kinder) haben.
    Wenns wirklich nur ein Pointer sein soll, dann deklariere das Template, ohne es zu definieren und spezialisiere es dann partiell für Pointer. Bei der Spezialisierung kannst du dann wie im ersten Fall fortfahren.
    ODER du lässt bei der Pointergeschichte den Templatekram einfach weg, benutzt einen Zeiger auf die Basisklasse und verwendest deren Interface - das kann dann genauso mit Zeigern auf die abgeleiteten Klassen verwendet werden (Laufzeit-Polymorphie)



  • Was ich gesehen habe ist, dass man statt class z.B. int oder fload oder als Typen-Parameter verwenden kann.

    Anstelle des Sclüsselwortes class kann auch typename verwendet werden. Es ist identisch.

    bsp:

    template <typename T>
    class A
    {
    };
    

    So ist die Verwirrung ev. etwas kleiner.



  • Was ich gesehen habe ist, dass man statt class z.B. int oder fload oder als Typen-Parameter verwenden kann. Und ich habe auch gelesen,dass es möglich sein sollte, dass man z.B. nur Subklassen einer bestimmten Basisklasse als Parameter mitgeben kann, nur hab ich nicht herausgefunden, wie man das mach..!?

    Konkret interessieren mich folgende Fälle:

    - Nur Subklassen einer bstimmten Basisklasse sind erlaubt (und die Basisklasse selbst natürlich)
    - Nur Zeiger auf bestimmte Basisklasse und deren Subklassen sind erlaub

    Was ist denn der Grund einer solchen Einschränkung?
    Gruss Simon



  • Danke für die vielen und ausführlichen Antworten 🙂 Eigentlich ging es mir ursprünglich um folgendes:

    Wenn ich ein template erstelle, bei dem ich weiss, dass es nur für Zeiger funktioniert (weil ich z.B. irgendwo delete verwende), dann hätte ich diese Restriktion gerne auf irgendeine formelle Art im Source hinterlegt. So dass der Kompiler merkt, dass eine Template-Instanzierung mit diesem anderen Typ nicht möglich ist und eine Entsprechende Meldung ausgibt. Ansonsten wir der Compiler vermutlich merken, dass irgendwelche operationen verwendet werden, die vom entsprechenden Typ nicht supported werden und irgend eine verwirrende Meldung produzieren.. dann merkt man natürlich auch, dass es nicht geht 🙂

    Aber so wie ich es verstanden jetzt habe, ist der approach schon, dass man das Template schreibt, alles verwendet, das man so braucht:

    - delete, Zeiger-Operationen, wenn man davon ausgeht, dass das Template mit einem Zeiger-Typ instanziert wird
    - Operatioren wie == usw., wenn man davon ausgeht, dass das Template mit einem Typ instanziert wird, der den entsprechenden Operator implementiert
    -Irgendwelche Funktionen, wenn man davon ausgeht, dass das Template mit einem Typ instanziert werde, der die entsprechende Funktion implementiert

    und bei der Tamplate-Instanzierung schaut man dann einfach, was herauskommt, wenn man den abstrakten typ mit dem konkreten ersetzt .. ?

    Grüsse, Dominic



  • delete Operationen dürfen nur dann ausgeführt werden wenn man sicher ist, dass das entsprechende Objekt mit new (auch nicht mit malloc) erzeugt wurde (oder 0 ist). Wenn nun in einem Template nur delete angewendet wird (z.B. mit der Idee in einer Liste alle Nodes zu löschen), dann finde ich, setzt das sehr viel spezifisches Wissen über die Template Impl. vorraus (z.B. dass die Objekte mit new (mit malloc geht dann nicht) auf dem Heap erzeugt worden sind. Oder dass nicht einfach Pointer von Objekten auf dem Stack vom Template verwaltet werden können).

    Alles in allem würde ich sagen: Wenns geht, new und delete in der gleichen "Einheit" verwenden - hier meine ich mit Einheit z.B. das Listen Template. Man könnte der Einheit auch Verantwortlichkeit sagen...

    Ausnahme wäre hier z.B. einen Smartpointer, bei dem ein Pointer übergeben wird, der auf ein Objekt auf dem Heap zeigt. Im Smartpointer wird dann (z.B. mit Hilfe Ref. Counting) das entsprechende Objekt wieder gelöscht. Hier sind natürlich new und delete nicht im selben Template.

    Übrigens kann deine Speicher Problemmatik mit Smartpointern auch gerade gelöst werden, indem Du einfach Smartpointer anstelle von nackten Pointern verwendest.

    und bei der Tamplate-Instanzierung schaut man dann einfach, was herauskommt, wenn man den abstrakten typ mit dem konkreten ersetzt .. ?

    Ja, z.B. ob Methoden die aufgreufen werden auch wirklich vorhanden sind.

    Ich hoffe ich habe nicht zu wirr geschrieben.

    Gruss Simon


Log in to reply