Template-Parameter muss Numeretisch sein



  • Hallo
    Gibt es eine Möglichkeit, mit der man bestimmen kann, ob ein Typ der als Template-Parameter angegeben wurde numeretisch oder nicht-numeretisch ist?



  • Was soll das Word "numeretisch" bedeuten?
    Meinst du grundsätzlich Zahlentypen? (float, int, bool, char, ...)
    Oder nur integrale Typen? (ohne float und double)

    Google ggf. mal nach "type traits", da gibts z.B. was von boost.



  • Alle Zahlentypen, jedoch auch selbstgeschrieben, die die erforderlichen Operatoren bereitstellen.


  • Mod

    EOutOfResources schrieb:

    Alle Zahlentypen, jedoch auch selbstgeschrieben, die die erforderlichen Operatoren bereitstellen.

    Indem du diese Operatoren benutzt. Das kann man dann auch geschickt mit TMP kombinieren um darauf auch reagieren zu können. edit: Und das ist dann das was das von pumuckl genannte boost::type_traits macht 😃 .



  • SeppJ schrieb:

    Indem du diese Operatoren benutzt.

    Zur Erklärung: wenn du in einem Funktionstemplate (also auch Memberfunktionen von Klassentemplates) einen Operator benutzt und das Template mit einem Typ instantiierst, das den operator nicht anbietet, wirft der Compiler eine entsprechende Fehlermeldung. Bsp:

    template <typename T>
    class Plus
    {
      T t;
    public:
      Plus(T const& t_) : t(t_) {}
      T foo(T const& t2)
      {
        return t + t2;
      }
    };
    
    class myInt
    {
      int i;
    public: 
      explicit myInt(int i_) : i(i_) {}
    
      friend myInt operator+(myInt const& lhs, myInt const& rhs)
      {
        myInt r(lhs.i);
        r.i += rhs.i;
        return r;
      }
    };
    
    struct noInt {};
    
    int main()
    {
      noInt nI;
      myInt mI(15);
    
      Plus<int> p_int(12);
      int i_erg = p_int.foo(18); //ok, int hat op+ eingebaut
    
      Plus<myInt> p_myInt(mI); //OK
      myInt mI_erg = p_myInt.foo(myInt(42)); //ok, myInt hat op+
    
      Plus<noInt> p_noInt(nI); //OK, braucht keinen op+
      noInt nI_erg = p_noInt.foo(nI); //FEHLER: operator+(noInt, noInt) nicht definiert
    }
    


  • Eventuell so?

    #define ASSERT_INTEGER(type) { type val; int integer = val }
    


  • Ethon schrieb:

    Eventuell so?

    #define ASSERT_INTEGER(type) { type val; int integer = val }
    

    Nein. Das wirft (wenn du nach dem zweiten "val" noch das fehlende Semikolon ergänzt) nur dann keinen Fehler, wenn das Argument 1) default-constructible ist und 2) in einen int konvertiert werden kann. Hat nichts damit zu tun, dass bestimmte Operatoren vorhanden sein müssen.



  • Ach, ich war gerade total drauf fixiert, dass nur built-in Typen in Frage kommen.



  • SeppJ schrieb:

    Das kann man dann auch geschickt mit TMP kombinieren um darauf auch reagieren zu können. edit: Und das ist dann das was das von pumuckl genannte boost::type_traits macht 😃 .

    Haben die Type-Traits Tricks auf Lager, welche das Vorhandensein von Operatoren überprüfen? Handelt es sich nicht nur um eine Templatespezialisierung für die einzelnen BuiltIn-Typen?

    Um eigene Typen zu erkennen, muss man wahrscheinlich selbst eine Spezialisierung vornehmen. Ich fände es jedenfalls unintuitiv, falls das irgendwie automatisch erkannt würde. Zumal es sicher Fälle gäbe, in denen ein Typ falsch erkannt wird.

    Übrigens: numerisch


  • Mod

    Nexus schrieb:

    SeppJ schrieb:

    Das kann man dann auch geschickt mit TMP kombinieren um darauf auch reagieren zu können. edit: Und das ist dann das was das von pumuckl genannte boost::type_traits macht 😃 .

    Haben die Type-Traits Tricks auf Lager, welche das Vorhandensein von Operatoren überprüfen? Handelt es sich nicht nur um eine Templatespezialisierung für die einzelnen BuiltIn-Typen?

    Boost hat's nicht. Ich habe auch mal rumprobiert. Die eingebauten Typen und Operatoren als Memberfunktionen zu erkennen ist kein Problem (Templatespezialisierung für die eingebauten Typen und SFINAE-Tricks für Memberfunktionen). Ich tue mich aber schwer dies mit einer Möglichkeit zu verwursten, freie Funktionen zu erkennen. Das kann man zwar auch hinbekommen wenn man ein konkretes Objekt hat, aber einfach nur mit einem Typennamen bekomme ich es nicht hin.
    Da boost das nicht bietet ist anzunehmen, dass die Programmierer dort auch keine Lösung für dieses Problem gefunden haben 😞 .



  • SeppJ schrieb:

    Ich tue mich aber schwer dies mit einer Möglichkeit zu verwursten, freie Funktionen zu erkennen. Das kann man zwar auch hinbekommen wenn man ein konkretes Objekt hat, aber einfach nur mit einem Typennamen bekomme ich es nicht hin.

    An dem sollte es nicht scheitern. Ich habe kurz herumexperimentiert, kann man sicher noch verbessern, in einem Namensraum einpacken (um + zu verstecken) und für BuiltIns spezialisieren:

    struct Dummy { char n[347]; };
    
    template <typename T>
    T MakeT();
    
    template <typename T>
    Dummy operator+(T, T);
    
    template <typename T>
    struct HasOperatorPlus
    {
    	static const bool value = sizeof(MakeT<T>() + MakeT<T>()) != sizeof(Dummy);
    };
    

    Aber ich finde es dennoch besser, die Dinge zu hardcoden. Denn das Vorhandensein von Operatorüberladung sagt zunächst nichts über die Highlevel-Semantik aus. std::string hat auch operator+ , ist aber nicht numerisch.

    Im anderen Fall ist es ja "leicht", etwas Intelligenteres darum herum zu basteln. Besser, wenn die standardisierten type_traits einfachen Regeln folgen.


  • Mod

    Nexus schrieb:

    Aber ich finde es dennoch besser, die Dinge zu hardcoden. Denn das Vorhandensein von Operatorüberladung sagt zunächst nichts über die Highlevel-Semantik aus. std::string hat auch operator+ , ist aber nicht numerisch.

    Eigentlich schon. Operator + ist zwar vorhanden, aber dafür nicht *, /, -. Die Anwedenheit aller vier ist schon ein sehr starkes Anzeichen für etwas was sich wie eine Zahl verhält. Wenn jemand all diese Operatoren für andere Semantik misbraucht UND diese dazu noch ein Template für numerische Typen benutzt, der muss selber wissen auf was er sich da einlässt.

    Hübsch kurzer Code übrigens 👍 . Mein Ursprungsansatz wurde einfach nur immer komplizierter bis gar nichts mehr zu machen war.



  • Andererseits kann es sein, dass nur ein Teil der Funktionalität benötigt wird, und dann wäre Duck-Typing wieder geeigneter (denn nicht aufgerufene Funktionstemplates werden nicht instanziiert). Also dass der operator+ (oder was auch immer) implizit verwendet wird, und bei Nicht-Verfügbarkeit-aber-trotzdem-Verwendung gibts einen Compilerfehler.

    Oder gibt es einen Grund, warum du (EOutOfResources) sowas explizit erzwingen willst?

    SeppJ schrieb:

    Hübsch kurzer Code übrigens 👍

    Danke. Ich glaube, die Idee ist ursprünglich von Alexandrescu. Schau einfach, dass bei dir keine Klasse 347 Bytes gross ist 😉
    (oder implementier es flexibler)



  • Nexus schrieb:

    Oder gibt es einen Grund, warum du (EOutOfResources) sowas explizit erzwingen willst?

    Für eine mathematische Container-Klasse. Jedoch würde ein "einfach nutzen und auf Fehler warten" in diesem Fall vollkommen reichen...



  • Dann mach doch das. 🙂

    Kennst du übrigens std::valarray ? Das Klassentemplate gehört wohl zu den unbekanntesten der Standardbibliothek...



  • Nexus schrieb:

    Dann mach doch das. 🙂

    Kennst du übrigens std::valarray ? Das Klassentemplate gehört wohl zu den unbekanntesten der Standardbibliothek...

    Es ist Teil einer Schnittstelle. Zudem nutzt es intern schon einen std::vector . Die Klasse gibt es nur um die Daten zu verarbeiten...


Anmelden zum Antworten