Template: Klasse je nach Typ teilweise übersetzen?
-
Hallo! :xmas2:
Ein Besipiel:template<class T, const int n> struct foo { foo(T a=1, T b=2); //Wenn n<=2 foo(T a=1, T b=2, T c=3); //Wenn n>=3 T dat; };die beiden konstruktoren überlappen sich ja, da sie Standardargumente besitzen. Ist es nun irgendwie möglich dass für (beispielsweise) foo<double, 2> nur der erste und für foo<double, 3> der letzte konstruktor übersetzt wird?
Danke :xmas1:
-
nein, da es die template idee von c++ nicht hergibt, dass du mittels parameter die richtigen funktionen auswählen kannst und so dein compiler die beiden ctor-varianten nicht unterscheiden kann, solange du nur 2 argumente angibst. danach könnte er, er wird sich aber vorher schon beschweren.
:xmas2:
-
ghorst schrieb:
nein, da dein compiler die beiden ctor-varianten nicht unterscheiden kann, solange du nur 2 argumente angibst. danach könnte er, er wird sich aber vorher schon beschweren.
:xmas2:
Geht da nicht irgendwas mit Makros? Oder irgendwelche anderen Lösungsmöglichkeiten? :xmas1://Edit:
Nein, ich meinte eigendlich dass C++ niemals in den Konflikt kommt zwischen den beiden auszuwählen, da vll Makros(?
:xmas1:
??) den nicht gebrauchten einfach eleminieren
^^
-
mit makros kann man so etwas bauen. allerdings geht das nicht auf basis der templateargumente. der präprozessor agiert auf textersetzungsbasis und kommt vor dem eigentliche compiler, der aber erst ahnung von templates hat. :xmas2:
-
Du kannst das Template partiell spezialisieren - das wird in der Regel nicht praktikabel sein, denn das führt zu viel Duplizität. Oder aber in Abhängigkeit der Templateparameter nur bestimmte Überladungen zulassen - Stichwort SFINAE.
z.B.template<class T, const int n> struct foo { foo(typename enable_if_c< (n <= 2), T >::type a=1, T b=2); foo(typename enable_if_c< (n > 2), T >::type a=1, T b=2, T c=3); T dat; };
-
Meine Klasse ist nur vernünftig zu benutzen wenn es diese konstruktoren gibt... bleibt mir also nichts anderes übrig als die klasse x mal zu kopieren für die verschiedenen "n"?
//Edit: campers beitrag bedarf einiger bedenkzeit für mich ^^ (noch nie was von gehört) .. danke erstmal für die bisherigen antworten! :xmas1: :xmas2:
-
@camper
wieder was gelernt.
:xmas2:
-
@camper... sehr interessant! Ty! ABER ...
... warum geht das nicht so?#include <iostream> #include <boost/utility/enable_if.hpp> using namespace std; template<class T, const int n> class foo { public: foo(typename boost::enable_if_c<(n == 2), T>::type a=1, T b=2){ dat[0]=a; dat[1]=b; cout << "n==2" << endl; } foo(typename boost::enable_if_c<(n > 2), T>::type a=1, T b=2, T c=3){ dat[0]=a; dat[1]=b; dat[2]=c; cout << "n==3" << endl; } void print(){ for(int i=0; i<n; i++) cout << dat[i] << " "; cout << endl; } T dat[n]; }; typedef foo<int, 2> foo2i; //typedef foo<int, 3> foo3i; int main() { foo2i v(4,9); v.print(); // foo3i w; }Wenn der zweite konstruktor nicht auskommentiert ist, lässt sich nichts kompilieren:
boost.cpp: In instantiation of »foo<int, 2>«:
boost.cpp:31: instantiated from here
boost.cpp:13: Fehler: kein Typ namens »type« in »struct boost::enable_if_c<false, int>«
-
Weil ich immer wieder den gleichen Fehler mache. SFINAE erlaubt es, aus einem Überladungsset diejenigen Templates zu eliminieren, die für die gegebenen/deduzierten Templateparameter nicht instantiierbar ist. Die Konstruktoren hier sind aber keine Templates, sondern gewöhnliche Funktionen - dass sie selbst Member eines Klassentemplates sind, ist dabei unerheblich. Also muss man sie künstlich in ein Template verwandeln, z.B.
template<class T, const int n> struct foo { foo(); // geht nicht als Template, da wir Templateparameter von Konstruktoren nur deduzieren können, und das entfällt bei 0 Parametern offensichtlich foo(T a); template<typename U> foo(U a, typename enable_if_c< is_convertible< U, T >::value && ( n <= 2 ) >::type b); template<typename U> foo(U a, typename enable_if_c< is_convertible< U, T >::value && ( n > 2 ) >::type b, T c = 3); // von hier an wird es einfachEine andere Lösung dieses speziellen Problems könnte natürlich auch darin liegen, ohne Defaultargumente die Konstruktoren für 0,1,2... Parameter zu erstellen und die Behandlung der verschiedenen n in die Implementation zu verschieben, mit den Mitteln von C++0x wird das leichter werden...
-
Eine andere lösung ist mittels boost preprocessor sich dann eine "prototyp" überladung als makro zu schreiben, und dann 1-x spezialisierungen zu erstellen.
-
spricht was gegen folgendes:
template <class T, int n> class FooImpl { /* gemeinsame Funktionen und Member für alle n */ /* Member auf die die Ctoren zugreifen protected */ }; template <class T> class FooImpl<T,0>; //nicht definiert um unsinnige n zu vermeiden template <class T, int n> class Foo : public FooImpl<T, (n>0?n:0)> { //falls n <= 0 wird von einer nicht definierten Klasse abgeleitet -> Compiletime error public: Foo(T a=1, T b=2, T c=3); }; template <class T> class Foo<T, 2> : public FooImpl<T,2> { public: Foo(T a=1, T b=2); }; template <class T> class Foo<T, 1> : public FooImpl<T,1> { public: Foo(T a=1); };Man kann natürlich die Instatiierung für negative n auch verhindern indem man statt int size_t als templateparameter verwendet. Wollte nur aufzeigen, wie man unerwünschte n vermeiden kann.