templates Parameter vorgeben



  • Hallo,

    ich bin mir jetzt nicht sicher ob im C++0x etwas davon bereits steht. Ich nutze VS2010. Ist es möglich template-Parameter auszuschließen bzw. anzugeben?

    template<class T>
    class Nummber
    {
        // ...
    };
    

    Bei dieser Klasse macht es keinen Sinn, dass T ein char oder gar std::string ist. Lediglich short/int/long etc. sind gültig.

    lg
    Ralf



  • Jein. Du kannst einzelne Typen nicht ausschliessen, aber durch Spezialiasierung kannst du nur bestimmte Typen zulassen. Dazu musst du die template Klasse ungültig machen, sodass keine Instanz erzeugt werden kann:

    // nicht benutzbar
    template<typename T>
    class Number
    {  
       // Compiler erzeugt Fehler
       static const int NO_IMPLEMENTATION_FOR_THIS_TYPE[-1];
    
       // oder wenn du boost benutzt
       BOOST_STATIC_ASSERT( false );
    }
    
    // Implementation
    template<>
    class Number<int>
    {
      ...
    }
    
    int main()
    {
       Number<int> Success;
       Number<bool> Failure;
    }
    


  • Ein 'char' ist aber auch eine Zahl, wenn auch nur (bei Standard 8bit-Systemen) von -128 bis 127 (bzw. 0 bis 255).

    Edit: weil krümelkacker seinem Namen alle Ehre macht 😉


  • Mod

    Das ganz dürfte auch mehr oder weniger automatisch erfolgen. Wenn T eine Zahl sein muss, dann heißt das ja, dass du in deinem Code irgendwo diese Zahleigenschaft benutzt, sprich: Man kann über die üblichen Operatoeren rechnen. Wenn nun jemand std::string einsetzt, dann gibt's einen (Compiler-)Fehler, weil std::string bestenfalls + als Operator kennt.
    Wenn du hingegen gar nicht ausnutzen solltest, dass Number eine Zahl ist (außer vom Namen her), dann gibt es auch keine guten Gründe dem Anwender das Einsetzen von std::string zu verbieten.



  • Ralf1 schrieb:

    ich bin mir jetzt nicht sicher ob im C++0x etwas davon bereits steht.

    Ich bin mir nicht sicher, ob ich den Satz verstanden habe.

    Ralf1 schrieb:

    Ich nutze VS2010. Ist es möglich template-Parameter auszuschließen bzw. anzugeben?

    template<class T>
    class Nummber
    {
        // ...
    };
    

    Bei dieser Klasse macht es keinen Sinn, dass T ein char oder gar std::string ist. Lediglich short/int/long etc. sind gültig.

    Bei so etwas mache ich mir gar nicht die Mühe, andere Typen explizit zu verbieten -- zumindest nicht bei einem Klassen-Template. Bei Funktions-Templates ist das schon praktischer, weil durch das Ausschließen von bestimmten Template-Parameter-Belegungen die Überladungsmenge klein gehalten werden kann. Wenn Du für T bei Deinem Template etwas einsetzt, was nicht all die Operationen unterstützt, die Du abverlangst, gibt es eh einen Fehler zur Kompilierzeit. So gesehen funktioniert das mit den Template-Typ-Parametern wie Duck Typing zur Kompilierzeit.

    In C++0x könntest Du static_assert mit type_traits kombinieren:

    #include <type_traits>
    
    template<class T>
    class number
    {
      static_assert(std::is_integral<T>::value,"T muss ein Ganzzahltyp sein");
    };
    

    Das, was Du willst, nennt man im Englischen "constrained template". In C++0x hatte man dafür etwas vorgesehen ("concepts") und dann wieder entfernt, weil es noch nicht ausgereift war. Work-around für aktuelles C++ sind Dinge wie BOOST_STATIC_ASSERT für Klassen-Templates und boost::enable_if für Funktions-Templates.

    Th69 schrieb:

    Ein 'char' ist aber auch eine Zahl, wenn auch nur (bei Standard 8bit-Systemen) von -128 bis 127.

    Es gibt sicherlich auch 8-Bit-Systeme, bei denen der Wertebereich von char 0...255 ist. Zumindest erlaubt es der C++ Standard. Du weißt hoffentlich, dass char und signed char zwei verschiedene Typen sind, ja?



  • Du kannst dir auch mit Typlisten behelfen...

    #include "Typelist.h"
    
    #include <string>
    
    // Ich habe gerade kein Boost zur hand. Dass kannst/solltest du mit BOOST_STATIC_ASSERT machen
    template <bool condition>
    class Assert
    {
    
    };
    
    template <>
    class Assert<false>
    {
    	private:
    		Assert(){};
    };
    // BOOST_STATIC_ASSERT Ende
    
    template <typename Type>
    class Number
    {
    	// definiere die Typen die nicht erlaubt sind.
    	typedef Loki::TL::MakeTypelist<char, std::string>::Result NotAllowedTypes;
    
    	// prüfe ob der Typ erlaubt ist, oder nicht. (Machs mit BOOST_STATIC_ASSERT)
    	Assert< (Loki::TL::IndexOf<NotAllowedTypes, Type>::value == -1) > check;
    
    };
    
    int main(int argc, char* argv[])
    {
    	// kein fehler
    	Number<int> a;
    
    	// compiler fehler.
    	Number<std::string> b;
    
    	return 0;
    }
    

    Die Typlisten gibt es in der Loki Bibliothek

    Du kannst auch auf erlaubte Typen prüfen, wenn dir das lieber ist, einfach eine Typliste mit erlaubten Typen erstellen und in der Assert Bedingung auf != -1 prüfen.

    Greets
    Tobi



  • Tobias Gerg schrieb:

    ...

    Naja, wenn man eh schon boost benutzt (das BOOST_STATIC_ASSERT impliziert das), dann kann man auch gleich die Typlisten aus boost benutzen. 😉



  • Tachyon schrieb:

    Tobias Gerg schrieb:

    ...

    Naja, wenn man eh schon boost benutzt (das BOOST_STATIC_ASSERT impliziert das), dann kann man auch gleich die Typlisten aus boost benutzen. 😉

    Klar... 😉 Nur wollte ich das Beispiel als Code hinschreiben und da ich Loki da habe und Boost nicht.... :p 🤡 😉

    Greets



  • Man kann das in C++0x auch ohne boost mit static_assert() und SFINAE lösen (jedenfalls sinnvoll, wenn eine Nicht-Zahlen-Klasse alle Zahlenoperatoren überlädt, funktioniert es trotzdem nicht).


Anmelden zum Antworten