Templates: Wie kann ich eine Klasse via z.B. enable_if für mehrere Typen gleichzeitig spezialisieren?



  • Hallo,

    ich zerbreche mir gerade den Kopf an folgender Frage: Ich weiß, dass man z.B. mit

    template<class T>
    class Vector {
    // ...
    };
    
    template<>
    class Vector<bool> {
    // ....
    };
    

    speziell für den Fall Vector<bool> eine eigene Implementierung machen kann.

    Angenommen ich möchte für alle vector<T>, wo T z.B. ein built-in type außer bool ist, eine andere Implementierung als für normale vector<T>, und dabei nicht den Code für int, unsigned int, short, unsigned short etc. duplizieren: Wie schaffe ich das? Meine Überlegung war irgendwie mit std::enable_if und z.B. is_arithmetic, ich weiß aber nicht wie ich das für Klassen anwenden kann. Meine Überlegung ist irgendwie über einen zusätzlichen template Parameter, aber ich bekomme es nicht hin.

    Ich hoffe meine Frage ist verständlich. Vielen Dank im Voraus.

    LG


  • |  Mod

    Ein zusätzlicher Template-Parameter ist die Standardvariante.

    template <typename T, typename = void>
    class Vector { .. };
    
    template <typename T>
    class Vector<T, std::enable_if_t<...>> { ... };
    

    Hübscher wird das ganze dann mit Concepts, wenn man die partielle Spezialisierung mittels einer requires clause spezialisieren kann.



  • // edit: too late ... da traut man sich einmal was über Templates zu schreiben, und bumm, ist sone verdammter h4x0r auch munter 😞
    //edit2: enable_if_t<> hab ich auch verschlafen 😞 😞

    #include <string>
    #include <iostream>
    #include <type_traits>
    
    template <typename T, typename Enable = void>
    class Vector
    {
    public:
    	Vector() { std::cout << "Other\n"; }
    };
    
    template <typename T>
    class Vector<T, std::enable_if_t<std::is_arithmetic<T>::value>>
    {
    public:
    	Vector() { std::cout << "Float or Integral \n"; }
    };
    
    template <>
    class Vector<bool>
    {
    public:
    	Vector() { std::cout << "Boolean\n"; }
    };
    
    int main()
    {
    	Vector<int> vi;
    	Vector<bool> vb;
    	Vector<float> vf;
    	Vector<std::string> vs;
    }
    

    -->

    Float or Integral
    Boolean
    Float or Integral
    Other
    


  • Hier noch ne Spielerei wenns mal sehr speziell wird und man die Template-Argumente dennoch übersichtlich halten will:

    #include <type_traits>
    #include <string>
    #include <iostream>
    
    template <typename T, typename...>
    struct is_one_of : std::false_type {};
    template <typename T, typename U0, typename... Us>
    struct is_one_of<T, U0, Us...> : std::conditional_t<std::is_same<T, U0>::value, std::true_type, is_one_of<T, Us...>> {};
    
    template <typename T>
    auto test(const T& arg) -> std::enable_if_t<is_one_of<T, bool, float, int>::value>
    {
        std::cout << std::boolalpha << arg << " is either bool, float or int" << std::endl;
    }
    
    template <typename T>
    auto test(const T& arg) -> std::enable_if_t<is_one_of<T, char, double, unsigned>::value>
    {
        std::cout << arg << " is either char, double or unsigned" << std::endl;
    }
    
    template <typename T>
    auto test(const T& arg) -> std::enable_if_t<is_one_of<T, std::string>::value>
    {
        std::cout << arg << " is a string." << std::endl;
    }
    
    auto main() -> int
    {
        test(true);
        test(-1);
        test(1.0f);
        test('a');
        test(3.0);
        test(1U);
        test(std::string{"abc"});
        return 0;
    }
    

    P.S.: Hab ich bei den Code-Tags was vergessen oder hab ich mal wieder wie schon in diversen IDEs mit wilden Templates das Syntax-Highlighting zerschossen? 😉



  • @finnegan sagte in Templates: Wie kann ich eine Klasse via z.B. enable_if für mehrere Typen gleichzeitig spezialisieren?:

    auto main() -> int
    

    *hrhr*

    auto main() -> decltype(EXIT_SUCCESS)
    

    ?



  • @swordfish sagte in Templates: Wie kann ich eine Klasse via z.B. enable_if für mehrere Typen gleichzeitig spezialisieren?:

    *hrhr*

    Ist ne Angewohnheit von mir alle Return-Typen außer void hinten dranzusetzen. Hat den Vorteil dass in den ersten Editor-Spalten immer nur

    auto
    void
    

    steht. Daher beginnen alle Funktionsnamen (IMHO die wichtigste Information) in der selben Spalte. Sieht sauber aus und ist gut lesbar wie ich finde. Bei der main() siehts natürlich etwas Banane aus, aber damit kann ich leben 😉



  • @finnegan sagte in Templates: Wie kann ich eine Klasse via z.B. enable_if für mehrere Typen gleichzeitig spezialisieren?:

    Daher beginnen alle Funktionsnamen (IMHO die wichtigste Information) in der selben Spalte.

    Ja, dieses Argument dafür hab ich schonmal gehört.



  • @finnegan sagte in Templates: Wie kann ich eine Klasse via z.B. enable_if für mehrere Typen gleichzeitig spezialisieren?:

    P.S.: Hab ich bei den Code-Tags was vergessen oder hab ich mal wieder wie schon in diversen IDEs mit wilden Templates das Syntax-Highlighting zerschossen? 😉

    Versuchs mal mit ```c++

    @finnegan sagte in Templates: Wie kann ich eine Klasse via z.B. enable_if für mehrere Typen gleichzeitig spezialisieren?:

    #include <type_traits>
    #include <string>
    #include <iostream>
    
    template <typename T, typename...>
    struct is_one_of : std::false_type {};
    template <typename T, typename U0, typename... Us>
    struct is_one_of<T, U0, Us...> : std::conditional_t<std::is_same<T, U0>::value, std::true_type, is_one_of<T, Us...>> {};
    
    template <typename T>
    auto test(const T& arg) -> std::enable_if_t<is_one_of<T, bool, float, int>::value>
    {
        std::cout << std::boolalpha << arg << " is either bool, float or int" << std::endl;
    }
    
    template <typename T>
    auto test(const T& arg) -> std::enable_if_t<is_one_of<T, char, double, unsigned>::value>
    {
        std::cout << arg << " is either char, double or unsigned" << std::endl;
    }
    
    template <typename T>
    auto test(const T& arg) -> std::enable_if_t<is_one_of<T, std::string>::value>
    {
        std::cout << arg << " is a string." << std::endl;
    }
    
    auto main() -> int
    {
        test(true);
        test(-1);
        test(1.0f);
        test('a');
        test(3.0);
        test(1U);
        test(std::string{"abc"});
        return 0;
    }
    


  • Hallo,

    vielen Dank für Eure zahlreichen Antworten. Wieder mal was gelernt 👍