remove_const für Template mit Spezialisierung



  • Die Weisheit ist, dass, wenn du Zeile 14 in

    class Y<const T<TA>, ...
    

    änderst, die Ausgabe 1 2 ist und nicht mehr 2 1.



  • Stimmt, Tim. Und wie löse ich mein Problem? 😞

    Edit: template<class> brauche ich, weil ich (1) qualifizierunabhängige Templates aber auch (2) qualifiziererunabhängige Templateargumente (in Kombination mit (1)) haben möchte.



  • Kurzer Fix:

    template<typename T> class O {
    public:
      typedef T type;
    };
    
    template<typename T>
    class Y<T,
            typename std::enable_if<std::is_same<typename std::remove_cv<T>::type,
                                                 O<float>>::value>::type>
    {
      typedef typename T::type TA;
     public:
      int get() const {return 2;}
    };
    


  • Okay, dankeschön!

    Und wie erweitere ich das dazu, dass der Template-Parametertyp inkl. seiner Qualifizierer egal ist? Denn das war mein eigentliches Ziel und darum bin ich überhaupt erst über die template<class>class-Schiene gegangen


  • Mod

    template <typename T>
    struct identity { using type = T; };
    template <typename T>
    using eval = typename T::type;
    
    template<typename T, typename Enable = void>
    class Y
    {
    public:
        int get() const {return 1;}
    };
    
    template<typename T> class O {};
    
    template <typename T> struct A;
    template <template <typename> class T, typename TA> struct A<T<TA>>
        : identity<T<eval<std::remove_cv<TA>>>> {};
    
    template<class T>
    class Y<T, eval<std::enable_if<std::is_same<eval<A<eval<std::remove_cv<T>>>>, O<float>>::value>>>
    {
    public:
        int get() const {return 2;}
    };
    


  • Wow, danke!

    Leider habe ich immer noch kein C++11-using zur Verfügung, was Zeilen 3-4 für mich nicht möglich machen, typedef kann ja sowas nicht, richtig? Funktioniert es auch ohne? 🙂

    Achso, vermutlich einfach mit:

    template<typename T, typename U>
    struct eval
    {
        typedef typename T::U type;
    };
    

    nebst Ersetzen von eval<.> durch eval<.>::type, richtig?



  • Eisflamme schrieb:

    Wow, danke!

    Leider habe ich immer noch kein C++11-using zur Verfügung, was Zeilen 3-4 für mich nicht möglich machen, typedef kann ja sowas nicht, richtig? Funktioniert es auch ohne? 🙂

    Ja, structe da mal drumrum 😉


  • Mod

    Dann wird es bloss ein bisschen länger. z.B.

    #include <type_traits>
    #include <iostream>
    
    template <typename T>
    struct identity { typedef T type; };
    
    template<typename T, typename Enable = void>
    class Y
    {
    public:
        int get() const {return 1;}
    };
    
    template<typename T> class O {};
    
    template <typename T> struct Analyze;
    template <template <typename> class T, typename TA> struct Analyze<T<TA>>
    {
        typedef TA type;
        template <typename X>
        struct apply
            : identity<T<X>> {};
    };
    
    template<class T>
    class Y<T, typename std::enable_if<std::is_same<
        typename Analyze<typename std::remove_cv<T>::type>::template apply<
            typename std::remove_cv<typename Analyze<typename std::remove_cv<T>::type>::type>::type>::type,
        O<float>>::value>::type>
    {
    public:
        int get() const {return 2;}
    };
    

  • Mod

    Eisflamme schrieb:

    Achso, vermutlich einfach mit:

    template<typename T, typename U>
    struct eval
    {
        typedef typename T::U type;
    };
    

    nebst Ersetzen von eval<.> durch eval<.>::type, richtig?

    Das ist hoffentlich ein Scherz.



  • Oh na ja, habe mich verlesen. Aber mir fehlt jetzt zu viel Verständnis, denn meine andere Lösung wäre auch nur mit ::U durch ::type ersetzt gewesen.

    Danke für den Code jedenfalls! Nutzt Du irgendwelche googlebaren Konzepte dafür, die ich vermutlich nicht kenne? (also SFNIAE ist mir prinzipiell erstmal bekannt, viel weiter komme ich wohl aber auch nicht 🙄) Erstmal verstehe ich ihn leider nicht, aber ich kann jetzt auch keine Erklärung von Grundkonzepten erwarten.


  • Mod

    Eigentlich ist die Lösung recht einfach, nur durch die umständliche Syntax sieht das komplizierter aus als es ist.

    Ein T<TA> (T ein Klassenmtemlate) produziert immer einen unqualifizierten Typen. Also muss Y erst einmal einen einfachen Typparameter haben, um sowohl const-qualifizierte als auch u-unqualifizierte Argumente anzunehmen (wenn man nicht 4 Versionen für alle cv-Varianten schreiben will).
    Dann muss die notwendige Zerlegung in Klassentemplate und Templateargument folglich an ein anderes Template delegiert werden, hier: Analyze. Diesem Template übergeben wir ein unqualifiziertes Argument, so dass die Zerlegung unproblematisch ist. Analyze hat dann entsprechende Member, um auf die Zerlegung zugreifen zu könne. apply fungiert dabei als Pseude-Template-typedef, analog zu ::other in Allokatoren (mit using-Unterstützung würde man ein Aliastemplate verwenden).





  • Hi,

    okay, super. Habe es mehrfach gelesen und jetzt verstanden. 🙂

    Ich fand heraus, dass ich eigentlich etwas haben möchte wie is_same_template , was herausfindet ob für einen übergebenen Typ T das Template U verwendet wurde. Wobei bei einem übergebenem Typen const T<...> trotzdem ein match auf U (Template, non-const) passen soll.

    Und das will ich jetzt Mal auslagern, damit man es nicht jedes Mal neu schreiben muss. Jetzt habe ich aber ein Problem:

    #include <type_traits> 
    #include <iostream> 
    
    // Die Templates
    
    template<typename T> struct analyzer
    {
    	static const bool is_template = false; // mein kläglicher Versuch ihn davon abzuhalten den rechten Ausdruck von is_same_template::value auszuwerten
    };
    
    template<template<typename> class T, typename TA>
    struct analyzer<T<TA>>
    {
    	static const bool is_template = true;
    
    	typedef TA ptype;
    
    	template<typename U> struct apply {typedef T<U> type;};
    };
    
    template<typename T, template <typename> class U, typename Enable = void>
    struct is_same_template;
    
    template<typename T, template <typename> class U>
    struct is_same_template<T, U, typename std::enable_if<std::is_class<T>::value>::type>
    {
    	typedef analyzer<typename std::remove_cv<T>::type> analyzerT;
    
    	static const bool value = analyzerT::is_template && std::is_same
    	<
    		typename analyzerT::template apply<typename std::remove_cv<typename analyzerT::ptype>::type>::type,
    		U<typename std::remove_cv<typename analyzerT::ptype>::type>
    	>::value;
    };
    
    // Die Nutzung
    
    template<typename T> class O {};
    
    template<typename T, typename Enable = void> 
    class Y 
    { 
    public: 
        int get() const {return 1;} 
    }; 
    
    template<typename T>
    class Y<T, typename std::enable_if<is_same_template<T, O>::value>::type>
    {
    public:
    	int get() const {return 4;}
    };
    
    int main()
    {
    	struct NoTemplate {};
    
    	Y<NoTemplate> aMinus1;
    	Y<int> a0;
    	Y<O<int>> a1;
    	Y<O<float>> a2;
    	Y<const O<float>> a3;
    	Y<O<const int>> a4;
    	Y<const O<const float>> a5;
    	const Y<const O<const int>> a6;
    
    	std::cout << aMinus1.get() << '\n' << a0.get() << '\n' << a1.get() << '\n' << a2.get() << '\n' << a3.get() << '\n' << a4.get() << '\n' << a5.get() << '\n' << a6.get() << '\n';
    }
    

    aMinus1 kompiliert nicht. Zwar stellt er über is_template fest, dass es kein Template ist, jedoch wertet er den rechten Ausdruck von is_same_template::value trotzdem noch aus, was dann zum Fehler führt, da er ptype und apply nicht kennt. Soll ich da einfach ein Dummy-Interface nachbauen oder gibt es eine elegantere Lösung?

    Edit:
    Wieso kompiliert ideone das überhaupt für Y<int> a0; ? Für den Fall greift doch die Spezialisierung nicht, er fällt auf die Basisversion zurück und die hat kein value, ist das kein Compile-Error?



  • #include <type_traits>
    #include <iostream>
    
    template< typename T,
              template<class> class U >
    struct is_specialization_impl : std::false_type {};
    
    template< typename T,
              template<class> class U >
    struct is_specialization_impl<U<T>, U> : std::true_type {};
    
    template< typename T,
              template<class> class U >
    struct is_specialization : is_specialization_impl< typename std::remove_cv<T>::type, U > {};
    
    /// _________________________________________________________________
    
    template<typename T> class O {};
    
    template<typename T, typename Enable = void>
    class Y
    {
    public:
        static constexpr int get() {return 1;}
    };
    
    template<typename T>
    class Y<T, typename std::enable_if<is_specialization<T, O>::value>::type>
    {
    public:
        static constexpr int get() {return 4;}
    };
    
    int main()
    {
        struct NoTemplate {};
    
        static_assert( Y<NoTemplate>::get() == 1, "" );
        static_assert( Y<int>::get() == 1, "" );
        static_assert( Y<O<int>>::get() == 4, "" );
        static_assert( Y<O<float>>::get() == 4, "" );
        static_assert( Y<const O<float>>::get() == 4, "" );
        static_assert( Y<const O<const float>>::get() == 4, "" );
    }
    

    ?


  • Mod

    Die Frage, ob ein (zumindest potentiell ausgewerteter) Operand tatsächlich ausgewertet wird oder nicht ist nicht relvant dafür, ob dieser ggf. erst instantiiert werden muss. Dein Problem ist ganz klassich analog zu z.B.

    template <int N>
    struct fak
    {
        static const int value = N != 0 ? N * fak<N-1>::value : 1;
    };
    

    das auch nicht ohne zusätzliche Spezialisierung auskommt.

    std::is_same
        <
            typename analyzerT::template apply<typename std::remove_cv<typename analyzerT::ptype>::type>::type,
            U<typename std::remove_cv<typename analyzerT::ptype>::type>
        >::value;
    

    das ist komplizierter als es sein muss. Es kommt ja gar nicht darauf an, mit welchen konkreten Templateargument das Template gefüttert wird (und auch nicht, ob die entsprechende Spezialisierung instantiiert werden kann). Ein Test mit irgendeinem Typen, der bequem aufzuschreiben ist, reicht völlig aus (das wäre mit Aliastemplates anders, aber die können sowieso nicht selbst Templateargumente sein).

    std::is_same<typename analyzerT::template apply<int>, U<int>>::value
    

    Das ist dann wieder kurz genug, dass man es direkt ins enable_if schreiben kann.

    Edit: Sones Variante ist nat. schöner.



  • Sone schrieb:

    int main()
    {
        struct NoTemplate {};
    
        Y<NoTemplate>::get();
    }
    

    😮 Ist das seit C++11 erlaubt?



  • Ist das seit C++11 erlaubt?

    Wieso C++11? Das war schon immer erlaubt...

    Edit: Sones Variante ist nat. schöner.

    http://www.memes.at/faces/epic_win.jpg



  • 27 Sekunden! 🙂

    Sone schrieb:

    Ist das seit C++11 erlaubt?

    Wieso C++11? Das war schon immer erlaubt...

    Nein, das war vor deiner Zeit. In C++03 waren lokale Typen als Templateargumente noch verboten. Soll ich dir die Stelle im Standard raus suchen 😉



  • Sone schrieb:

    KasF schrieb:

    Sone schrieb:

    int main()
    {
        struct NoTemplate {};
    
        Y<NoTemplate>::get();
    }
    

    😮 Ist das seit C++11 erlaubt?

    Wieso C++11? Das war schon immer erlaubt...

    Das stimmt nicht. Funktionslokale Typen durften früher nicht als Template-Argumente verwendet werden.



  • Merkwürdig. Wo genau ist denn der konzeptuelle Unterschied von einer funktionslokalen Klasse zu einer Klasse, die nach dem Template deklariert(-finiert) wurde? Wieso hat C++03 da eine solches Verbot ausgehängt* 😕

    Aber du hast vollkommen Recht, das war vor dieser Ära in der ich "aufgewachsen" bin.

    ~* Ich habe das Gefühl, Herb Sutter hat bestimmt etwas dazu geschrieben.~


Anmelden zum Antworten