Templates: Vererbung des Template Typs?
-
Könnte mir vielleicht jemand beantworten: Ob es möglich ist, ein Template bei einer Funktion so zu definieren, dass auch alle Unterklassen des Typs für das Template gültig sind.
--
alex.partsch
-
Ich würde ausnutzen, daß Referenzen das können.
template<typename T> void foo(T& t){ machwas(t); }
-
@ alex.partsch: Könntest du deine Frage an einem kleinen Stück Code verdeutlichen?
-
alex.partsch schrieb:
Könnte mir vielleicht jemand beantworten: Ob es möglich ist, ein Template bei einer Funktion so zu definieren, dass auch alle Unterklassen des Typs für das Template gültig sind.
Bitte was? Von was für einem Typ sprichst Du und was genau meinst Du mit "gültig"?
-
Ich habe folgende Methode:
void QQueueThread::startWorking() { if(started) return ; // workers allready work this->workers = new QList<T *>; for(int cores = this->cores; cores > 0; cores--) { QQueueWorker *worker = new QQueueWorker(this->majorQueue, this); workers << worker; } // all workers are created ... start foreach(QQueueWorker *worker, this->workers) worker->start(); started = true; }diese macht nichts weiter als eine Liste zur erzeugen und Pointer auf QQueueWorker anzulegen ... jedoch sollten die QQueueWorker eine abstrakte Klasse sein; also dachte ich mir das man durch Templates, sozusagen den Typ der Klasse angeben könnte und ich dann aus dieser Information die Worker-Objekte hierfür erzeugen könnte!
Nur leider bin ich mir nicht ganz sicher wie ich das template so definiere, dass auch alle Unterklassen von QQueueWorker übergeben werden können.
Vielleicht kann dies noch etwas helfen:
In Java würde man es am besten so realisieren:// random ... public static <? extends CharSequence> T Add(T t1, T t2) { return t1.Concat(t2); }
-
Also meinst du sowas?
template<typename WorkerT> void QQueueThread<WorkerT>::startWorking() { if(started) return ; // workers allready work this->workers = new QList<WorkerT*>; for(int cores = this->cores; cores > 0; cores--) { WorkerT *worker = new WorkerT(this->majorQueue, this); workers << worker; } // all workers are created ... start foreach(WorkerT *worker, this->workers) worker->start(); started = true; }C++ Templates werden zur Kompilezeit ausgewertet. Der Kompiler schaut einfach, ob die Klasse die entsprechende Schnittstelle, welche man benötigt, auch anbietet. Das ist zur Kompilezeit für den Kompiler auch kein Problem. Da muss man nichts speziell angeben.
Grüssli
-
alex.partsch schrieb:
Vielleicht kann dies noch etwas helfen:
In Java würde man es am besten so realisieren:// random ... public static <? extends CharSequence> T Add(T t1, T t2) { return t1.Concat(t2); }Bitte programmiere kein Java in C++. Java Generics und C++ Templates haben auch nicht allzu viel miteinander zu tun. Eine solche "Java-generische" Funktion wird nur einmal übersetzt. Hier ist das "extends CharSequence" notwendig, damit sich die Funktion kompilieren lässt; denn sonst wüsste der Compiler nicht, was Du mit
Concatmeinst. In C++ ist ein Funktionstemplate keine Funktion, sondern eben -- *Trommelwirbel* -- ein Funktionstemplate, auf Deutsch: eine Funktions-Schablone. Zur Compile-Zeit wird/werden aus der Schablone null bis mehrere konkrete Funktionen für verschiedene Template-Parameter-Kombination erzeugt. Wenn dabei etwas nicht passt (zB gibt es keinT::Concat), bekommst Du eben einen Kompilierungsfehler während das Template instantiiert wird.Man kann aber, wenn man will, die Schablonen einschränken. Das kann zB dann sinnvoll sein, wenn man möchte, dass der Compiler das eine oder andere Funktions-Template ignoriert, falls bestimmte Bedingungen nicht erfüllt sind, so dass einige Funktionstemplates erst gar nicht in das "Overload resolution set" kommen. Das kann dann so aussehen:
template<class T> typename boost::enable_if< boost::is_convertible<T*,MeineBasisKlasse*> >::type foo(T* zeiger) { // Wir wissen hier, dass zeiger implizit // nach MeineBasisKlasse* konvertierbar ist, also // dass T entweder MeineBasisKlasse oder eine von // MeineBasisKlasse oeffentlich abgeleitete Klasse ist. ... }(Diesen Trick findet man unter dem Stichwort "SFINAE" -- substitution failure is not an error). Falls die Bedingung is_convertible<T*,MeineBasisKlasse*> nicht erfüllt ist, führt das Einsetzen vom deduzierten Typparameter T in die Funktionssignatur zu einem direkten Ersetzungsfehler, was den Compiler dazu veranlässt, das Template zu ignorieren.
-
Ich hätte das jetzt mit std::tr1::is_base_of gemacht. Mit is_convertible nimmt er dir auch void ab.
-
seldon schrieb:
Ich hätte das jetzt mit std::tr1::is_base_of gemacht. Mit is_convertible nimmt er dir auch void ab.
void* ist nicht implizit nach MeineBasisKlasse* konvertierbar, also kann T auch nie void sein.
Der Unterschied zwischen is_base_of und is_convertible müsste sich darauf beschränken, dass is_base_of auch private Vererbung erkennt.