Template :"Nur best Typen"



  • unskilled schrieb:

    ...das template ist für den Fall spezialisiert und für einen anderen Fall halt nicht definiert - hab noch nicht gelesen, dass das nicht gehen soll...bb

    Wie willst Du das durch Spezialisierung hinbekommen? Insbesondere den Teil, dass ein Template für die nichtspezialisierten Fälle nicht definiert ist?

    Spezialisierung ist nicht dafür da, die Benutzbarkeit eines Templates einzuschränken.
    Sie soll die Funktionalität für ein Subset von Typen in bestimmter Weise implementieren. Wenn Du nur zwei spezialisierte Templates zur Verfügung stellt, aber das allgemeine weglässt, dann kannst Du in der Tat auch normale Klassen benutzen.



  • Tachyon ich denke dieses einfach Template könnte deine Frage beantworten.

    template<bool> struct CTAssert;
    template<> struct CTAssert<true> {};
    

    Spezialisierung mit einer Forward-Deklaration kann aber dazu verwendet werden die Benutzbarkeit des Templates einzuschränken. Und in dem Beispiel oben ist es auch duraus sinnvoll. Auch wenn das Compilt-Time-Assert auch schöner imlementiert werden kann.

    Quelle: http://safari.ibmpressbooks.com/0201704315/pref01 und soweit ich weiss steht es auch in "Modern C++ Design", welches ich aber nur teilweise gelesen habe.

    Gruß
    Baracke



  • Baracke_out schrieb:

    [...]

    Das ist ein ganz anderer Sinnzusammenhang.

    Ich lasse mich allerdings gerne eines besseren belehren, wenn Du mir ein Beispiel für ein Template gibst (vielleicht ein Mini-Container), das mit genau zwei Typen ( double und struct MyType{}; ) funktioniert, sonst aber mit keinem anderen Typen.



  • Naja... Nicht ganz so elegant, aber es geht...

    template <class T>
    	class test
    		{
    			private:
    				test () {};
    		};
    
    template < >
    	class test <char>
    		{
    			public:
    				test () {};
    		};
    
    int main ()
    	{
    		test <bool> eins; //fehler
    		test <int> zwei; //fehler
    		test <char> drei; //geht
    	}
    

    Allerdings würde ich dann doch lieber meine Lösung von vorn mit der Exception bevorzugen ^^

    Aber recht geben muss ich dir dann ja wo trotzdem, für den Fall, dass du das nicht zählst ^^

    bb

    edit:
    mir ist noch ne lösung eingefallen - aber besser ist sie wahrscheinlich auch nicht xD
    wenn man dann die funktion noch iwie in "invalid_template_argument" umbennent könnte man es so gar fast als benutzerfreundlich zählen 😉

    void get_linker_error ();
    
    template <class T>
    	class test
    		{
    			public:
    				test ()
    					{
    						get_linker_error ();
    					};
    		};
    
    template < >
    	class test <char>
    		{
    			public:
    				test ()
    					{}
    		};
    
    int main ()
    	{
    		test <bool> eins;
    		test <int> zwei;
    		test <char> drei;
    	}
    

    PS: weiß nicht, ob #pragma da vll ne bessere möglichkeit bietet - aber das wär halt dann auch wieder msvc spezifisch...



  • unskilled schrieb:

    Naja... Nicht ganz so elegant, aber es geht...

    template <class T>
    	class test
    		{
    			private:
    				test () {};
    		};
    
    template < >
    	class test <char>
    		{
    			public:
    				test () {};
    		};
    
    int main ()
    	{
    		test <bool> eins; //fehler
    		test <int> zwei; //fehler
    		test <char> drei; //geht
    	}
    

    Allerdings würde ich dann doch lieber meine Lösung von vorn mit der Exception bevorzugen ^^

    Aber recht geben muss ich dir dann ja wo trotzdem, für den Fall, dass du das nicht zählst ^^

    bb

    Wie gesagt, ich hätte gerne einen Container für die beiden genannten Typen. Er soll mit seiner Template-Implementierung genau die zwei Typen zulassen. Alle anderen aber nicht. Die hier gezeigten Beispiele sind Spezialimplementierungen für die Spezialisierung.

    Dein Beispiel hat z.B. die allgemeine Implementierung mit dem Private-Ctor und eine spezialialisierte Version für char mit eigener Implementierung. Für weitere, zulässige Typen müsstest Du weitere Implementierungen bereitstellen.
    Du musst also für N Typen auch N Implementierungen spezialisieren, sowie denn allgemeinen Fall verbieten. Der Fall tes TOs ist sogar noch schlimmer, da die beiden Typen die gleiche Implementierung benutzen sollen. Man muss also zwei Spezialisierungen schreiben, die die gleichen Algorithmen beinhalten. Dann kann man auch gleich zwei Klassen schreiben, und sich das verbieten von nicht erwünschten Typen sparen.

    Mal ganz davon abgesehen sind, so wie ich das jetzt sehe, die beiden Typen mit denen der TO sein Template ausprägen will in Wirklichkeit nur ein Typ.



  • Hab noch ma Beitrag editiert ^^
    Und ja - das hatte ich ja vorhin schon bemerkt, dass es doch nur einer ist ^^

    Von daher ist das template vll wirklich ein wenig... wie soll ich sagen... fehl am platz ^^

    also hast ja recht 🤡

    bb



  • Nexus schrieb:

    @ Jihhaaaa:
    Wenn ein Template nur zwei Typen (typedefs) unterstützen soll und diese Typen dann auch noch identisch sind, ist eine normale, nicht-generische Klasse eher angebracht.

    Werde ich immer so überlesen? :p



  • weils sonst unfair mir gegenüber gewesen wäre ^^
    (ich war eher)

    unskilled schrieb:

    PS: warum verschiedene typedefs für gleiche typen? Oo

    :p :p :p



  • Aber ich war es, der als erster darauf hingewiesen hat, normale Klassen zu nehmen. :p

    Nein, jetzt mal im Ernst, warum sollte man in diesem Fall (1 zugelassener Typ) ein Template nehmen? Wie bist du auf die Idee gekommen, Jihhaaaa? 😉



  • Ich habe meinen vorletzten Beitrag nochmal erweitert (also den Beitrag vor diesem). 😃



  • Du musst also für N Typen auch N Implementierungen spezialisieren, sowie denn allgemeinen Fall verbieten.

    das müsste man bei richtigen Klassen auch - also N klassen für N typen (wobei ich nen kleines n nehmen würde xP )
    den allg Fall verbieten.. das is ja rel. schnell gemacht...
    also ich denke, es würde beim programmieren mit der klasse einfach besser aussehen - und für den compiler ist es wahrscheinlich egal, ob template oder eigene klasse...

    und wer will schon so ne klassen:

    class Taufgabe_int {};
    class Taufgabe_bool {};
    class Taufgabe_vector_const_iter_shouldnt_be_the_end_iterator_of_an_vector_or_deque_or_list_or_something_like_this {/*toll, oder : >*/};
    

    ^^

    bb



  • unskilled schrieb:

    das müsste man bei richtigen Klassen auch - also N klassen für N typen (wobei ich nen kleines n nehmen würde xP )

    Darum geht es doch. Wenn man eh nur N Fälle zulassen will, und die Menge N überschaubar bleibt, nimmt man besser Klassen anstatt Klassen-Templates. Man kann zweiteres natürlich schon nehmen, aber das ist irgendwie Schwachsinn.



  • ich würd die template-lösung (für mehr als einen typen ^^) der klassen-name-mit-variablen-typ-verunstalten-version vorziehen - selbst, wenn es mehr arbeit ist...



  • Tachyon schrieb:

    Darum geht es doch. Wenn man eh nur N Fälle zulassen will, und die Menge N überschaubar bleibt, nimmt man besser Klassen anstatt Klassen-Templates. Man kann zweiteres natürlich schon nehmen, aber das ist irgendwie Schwachsinn.

    Wenn sich die Klassen aber nur im Typ unterscheiden, kann man sich eine grosse Menge Code sparen, und als Nebeneffekt erhält man noch eine bessere Übersicht und Wartbarkeit. Ausserdem muss man nicht für jede Klasse den Bezeichner kennen.

    Im Notfall kann man ja immer noch typedefs machen (was ich aber sehr unschön finde)...



  • Nexus schrieb:

    Tachyon schrieb:

    Darum geht es doch. Wenn man eh nur N Fälle zulassen will, und die Menge N überschaubar bleibt, nimmt man besser Klassen anstatt Klassen-Templates. Man kann zweiteres natürlich schon nehmen, aber das ist irgendwie Schwachsinn.

    Wenn sich die Klassen aber nur im Typ unterscheiden, kann man sich eine grosse Menge Code sparen, und als Nebeneffekt erhält man noch eine bessere Übersicht und Wartbarkeit. Ausserdem muss man nicht für jede Klasse den Bezeichner kennen.

    Im Notfall kann man ja immer noch typedefs machen (was ich aber sehr unschön finde)...

    Nein, kann man eben nicht. Man kann nicht spezialisieren, ohne dass man für jede Spezialisierung eine Implementierung liefert.
    Entweder, man macht ein allgemeines Template, und lebt damit, dass jeder Typ erlaubt ist, oder man implementiert N mal spezialisiert. So oder so.
    Die beste Möglichkeit ist, denke ich, die von mir oben genannte PIMPL-Idiom-Variante.



  • Du hast natürlich Recht das der Zusammenhang nicht einwandfrei ist, ich wollte eigentlich nur auf dein
    "Wie willst Du das durch Spezialisierung hinbekommen? Insbesondere den Teil, dass ein Template für die nichtspezialisierten Fälle nicht definiert ist?
    Spezialisierung ist nicht dafür da, die Benutzbarkeit eines Templates einzuschränken." eingehen.

    Mini-Conatiner Beispiel. Mal versuchen...

    template<class T> class MiniContainerImpl {
    public:
         MiniContainerImpl() {...};
         ~MiniContainerImpl() {...};
    
         void add(T value) {...};
         void remove(T value) {...};
         T get(int index) {...}
    private:
         T *values;
    };
    
    template<class T> class MiniContainer;
    template<> class MiniContainer<double> : public MiniContainerImpl<double> { };
    template<> class MiniContainer<MyType> : public MiniConatinerImpl<MyTye>  { };
    

    Weiss net ob das so funktioniert. Habs net getestet, auch programmier ich schon länger nicht mehr in C++. Doch aus meiner logischen Sicht spricht nichts dagegen ausser diverse Kleinigkeiten die ich nimma genau weiss, wie Beispielweise das virtual vor dem Destruktor von der Basisklasse. Doch hab ich noch düster in Erinnerung, das es nicht empfehlenswert ist bei Templates.

    Klar kann man jetzt auch noch MiniContainerImpl direkt benützten, was ich finde immer noch nicht das ware ist. Aber was besseres ist mir auf die Schnelle nicht eigefallen.

    Hab auch nichts dagegen wenn du mich eines bessern belehrst 😉

    Gruß
    Baracke

    PS: Wie darf ich mir das PIMPL vorstellen, einen Pointer auf ne Forward deklartion im Classheader? Kannste mir vielleicht einen guten Link geben, könnte vielleicht ab und an nützlich sein.



  • Ne, geht nicht. Du benutzt Deine Impl nicht für nur zwei Typen, sondern machst Deine Impl für genau zwei Typen zu nichte. 😉



  • Tachyon schrieb:

    [...]

    Stimmt, da habe ich mir wohl zu wenig dabei überlegt.

    Mit normalen Klassen gibt es ja auch noch die Möglichkeit zur Vererbung. Oder eben die typedef -Variante, bei der man dann aber wieder das Template für alle Typen nutzen könnte, und die daher nicht wirklich sinnvoll ist. Das Template müsste eben nicht in seiner Grundform anwendbar sein (also versteckt oder so) 😉



  • Aber die Lösung find ich toll

    @tachyon: versteh deinen Einwand nicht...

    template <class T>
    	class test_
    		{ 
    			private: 
    				 T value; 
    			protected: 
    				 test_ (const T &a) {value = a;};
    			public:
    				 T get () {return value;}; 
    		}; 
    
    template <class T>
    	class test;
    
    template < >
    	class test <char>	: public test_ <char>
    		{
    			public:
    				test (char a) : test_ (a) {};
    		};
    
    int main ()
    	{
    		test <char> eins ('A');
    		char A = eins.get ();
    	}
    

    geht doch einwandfrei...

    bb



  • Die Lösung ist für den Fall nur suboptimal, da die Implementierung für den "Benutzer" noch frei zugänglich ist und somit zu Fehlern verleitet. Ne Doku kann das natrülich kompensieren. Trozdem nicht das gelbe vom Ei, zumal der Threadersteller im Prinzip nur einen Typ verwendet.

    Ja die Impl wird immer allgemein bleiben müssen, zumindestens eine soweit ich weiss. Der Clou wäre halt die Implementierung nicht zugänglich zu machen ausser von den Klassen, was man zwar mit privat and friend bewerkstelligen könnte. Doch müsste man jedesmal die Basisklasse anfassen wenn man noch einen Typ hinzufügen möchte. Kann gewünscht sein oder auch nicht.

    Wie auch immer, wenn du eine bessere Lösung für einen Container der nur endlich viele Typen akzeptiert hast nur her damit.

    Gruß
    Baracke


Anmelden zum Antworten