remove_const für Template mit Spezialisierung
-
Hi,
#include <type_traits> #include <iostream> template<typename T, typename Enable = void> class Y { public: int get() const {return 1;} }; template<typename T> class O {}; template<template<class> class T, typename TA> class Y<T<TA>, typename std::enable_if<std::is_same<typename std::remove_cv<T<TA>>::type, O<float>>::value>::type> { public: int get() const {return 2;} }; int main() { std::cout << Y<O<float>>().get() << ' ' << Y<const O<float>>().get(); std::cout << "\nTest: " << std::is_same<typename std::remove_cv<const O<float>>::type, O<float>>::value; }Wieso ist die Ausgabe hier "2 1\nTest: 1" und nicht "2 2\nTest: 1"?
Zur Erläuterung:
Ich möchte, dass die Spezialisierung für alleO<float>unabhängig der Qualifizierer fürO<float>greift. Zeile 25 des Codes liefert ja wie gewünscht true, nur die Spezialisierung greift hier nicht.
Woran liegt das?Danke und viele Grüße!
-
Weil
T<TA>ungleich
const T<TA>.
Generell sieht es nach einer falschen Anwendung von
template<class>aus.
-
Dafür ja mein Test, der aber true ergibt...
Und die Spezialisierung greift für Templateklassen an sich doch, wie man an der ersten 2 sieht. Hast Du solange gelesen und nachgedacht wie Deine Antwort ist oder steckt da doch mehr Weisheit hinter als ich erkennen kann?

-
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
-
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

-
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;} };
-
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.
-
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 Typenconst 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_templatefest, dass es kein Template ist, jedoch wertet er den rechten Ausdruck vonis_same_template::valuetrotzdem 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ürY<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, "" ); }?
-
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>>::valueDas 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.