Template nur Zahlen möglich



  • Hi

    Ich bin grad eine Klasse am schreiben, die ich gerne als Template realisieren möchte, so dass in der Implementierung zwischen int, double, float usw. ausgewählt werden kann.

    Ich glaube das geht standardmässig gar nicht, liege ich da richtig? Meine einzige Idee wäre Wrapperklassen zu schreiben, die von einer gemeinsam Klasse, z.B. Number, erben.

    Gibts da bessere Konzepte?



  • Natürlich geht das. Einfach ganz normal einen Template-Typ-Parameter ( typename oder class ) einrichten.



  • Aber dass NUR Zahlen als Parameter verwendet werden dürfen ohne doppelten Code zu verwenden. Geht das? Wenn ja vielleicht ein ganz kurzes Beispiel?


  • Mod

    icarus2 schrieb:

    Aber dass NUR Zahlen als Parameter verwendet werden dürfen ohne doppelten Code zu verwenden. Geht das? Wenn ja vielleicht ein ganz kurzes Beispiel?

    Definiere Zahl.

    Falls damit nur die eingebauten Datentypen (unsigned) char, short, int ,long, float und double meinst: Nein, das geht ohne extreme Tricksereien nicht. Aber warum sollte man dies überhaupt wollen?



  • Dafür gibts boost::enable_if.

    Allerdings würde ich das nicht unbedingt machen. Wenn ein User ein falsches Argument angibt, als du verlangst, dann ist er selber schuld, wenn es nicht kompiliert.

    Was spricht denn dagegen, dass man auch andere Parameter angeben kann?

    Ich vermute mal, dass du eine Art Vektor Klasse schreibst, wo man üblicherweise lediglich numerische Typen erwartet, was aber kein Grund ist andere Typen aussen vor zu lassen. Wenn ein Typ die erwarteten Operatoren und Funktionen anbietet, dann spricht auch nichts dagegen, dass er die Klasse dann auch benutzen kann.



  • Kannst du nicht einfach double nutzen? Da hättest du gar keinen doppelten Code.
    Denn Templates sind letztendlich auch nichts anderes als redundanter Code.

    #include <iostream>
    
    class Number
    {
    private:
    	double value_;
    
    public:
    	Number(double value) : value_(value) { }
    	Number(float value) : value_(value) { }
    	Number(int value) : value_(value) { }
    
    	double getDouble() const { return value_; }
    	float getFloat() const { return static_cast<float>(value_); }
    	int getInt() const { return static_cast<int>(value_); }
    };
    
    int main()
    {
    	Number n1(1), n2(2.1);
    
    	std::cout << n1.getInt() << ", " << n2.getDouble() << std::endl;
    }
    

    Aber letztendlich hängt es eh von der genauen Situation ab.



  • icarus2 schrieb:

    Aber dass NUR Zahlen als Parameter verwendet werden dürfen ohne doppelten Code zu verwenden. Geht das? Wenn ja vielleicht ein ganz kurzes Beispiel?

    #include <iostream>
    #include <string>
    using namespace std;
    
    template<typename T>
    class Foo{
    	static const T dummy=T()+T()*T();//Wird abkacken, wenn + und * nicht gehen
    	...//und hier der noemale Code
    };
    
    int main()
    {
    	Foo<double> g;
    //	Foo<string> s;
    	return 0;
    }
    

    Das erlaubt, daß dein Code noch mit Rational oder std::complex und so geht. Ich denke, das ist so beabsichtigt.



  • Naja... weil es in meinem Fall nicht wirlich sinnvoll ist andere Werte zu nehmen (kann sogar zu Fehlern führen).

    Da wollte ich fragen ob mein Ansatz schlecht ist, sowas in die Dokumentatino gehört oder wie man das sonst löst.

    Kurze Erklärung:
    Ich versuche gerade möglichst abstrakt eine Schnittstelle für das Bewegen von Objekten zu schaffen. Es soll dann erst in der Implementierung entschieden werden für welchen Typ man sich entscheidet.

    *Edit (Hab einige Beiträge erst jetzt gsehen)
    @drakon
    Ich schau mir das mal an. Es macht einfach nicht wirklich sinn da andere Parameter. Gut, vielleicht müsste da wirklich auch der, der die Implementierung schreibt daran denken.

    @mmmm
    Das wäre eigentlich eine Möglichkeit. Das einzige, was ich eigentlich nicht wollte war die value_ Variable private zu haben, aber eigentlich ist das gar nicht so schlecht. (Halt noch von Java an Interfaces gewohnt ^^)

    @volkard
    Durch das static const T dummy=T()+T()*T() wird dann verlangt, dass diese Operatoren unterstützt werden, richtig?



  • icarus2 schrieb:

    Da wollte ich fragen ob mein Ansatz schlecht ist, sowas in die Dokumentatino gehört oder wie man das sonst löst.

    Dokumentation ist da der übliche Weg.



  • Oki, dann mach ich das so.

    Danke für eure Beiträge.



  • Ah, so meinst du das. Ja, das geht auch, ist aber etwas komplizierter. Zuerst schreibst du dir mittels Template-Spezialisierung eine kleine Metafunktion, welche prüft, ob zwei Typen gleich sind:

    template <typename T, typename U>
    struct IsSame
    {
    	static const bool Value = false;
    };
    
    template <typename T>
    struct IsSame<T, T>
    {
    	static const bool Value = true;
    };
    

    Anschliessend noch ein Static-Assert, das eine Bedingung zur Kompilierzeit prüft (bei Condition == false wird ein Kompilierfehler ausgelöst, da das Template für diesen Fall nicht definiert ist):

    template <bool Condition>
    struct StaticAssert;
    
    template <>
    struct StaticAssert<true>
    {
    };
    

    Und dann irgendwo in der Art instanziieren ( sizeof schaut sich einen Ausdruck oder Typ an, ohne ihn komplett auszuwerten):

    sizeof( StaticAssert<
            IsSame<T, int>::Value
         || IsSame<T, double>::Value
         || IsSame<T, float>::Value > );
    

    Ist natürlich etwas viel Code, aber wenn du z.B. Boost hast, können dir die Type-Traits fast alles abnehmen. Da gibts noch Typlisten und jenste Möglichkeiten, aber das dürfte schonmal reichen. 😉



  • volkard schrieb:

    template<typename T>
    class Foo{
    	static const T dummy=T()+T()*T();//Wird abkacken, wenn + und * nicht gehen
    	...//und hier der noemale Code
    };
    

    Das funktioniert nur für integrale Typen, da Konstanten anderer Typen nicht in der Klassendefinition initialisiert werden dürfen. Also entweder in eine wichtige Methode packen (wichtig, weil der Compiler nicht benutze Funktionstemplates nicht vollständig analysiert und es daher durchgehen lassen könnte) und vielleicht mit sizeof den Dummy einsparen, oder direkt einen anderen Weg gehen.



  • Ahh, so langsam fange ich an dieses Metazeugs zu lieben. Es erfordert etwas anderes Denken, aber es scheint echt nützlich zu sein. 🙂



  • So, endlich mal wieder Zeit hier reinzuschauen.

    Habe gerade etwas Mühe das Beispiel zu verstehen, ist wirklich etwas kompliziert ^^

    Zum ersten Code-Snippet:
    Wenn die Typen nicht gleich sind, dann wird Value false (weil das obere IsSame zum Zug kommt). Sind sie gleich, dann wird das untere genommen und Value ist true?

    Zum zweiten Code-Snippet:
    Du sagst da einfach, dass die bool Condition true sein muss weil es für false nicht definiert ist?

    Zum dritten Code-Snippet:
    Sobald ein Value false ist, wird der gesamte Ausdruck false ==> Typen nicht gleich ==> Compilfehler?

    Habe ich das richtig verstanden?



  • Genau so ist es.



  • Alles klar, vielen Dank euch 🙂


Anmelden zum Antworten