remove_const für Template mit Spezialisierung
-
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.
-
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.~
-
Hi,
Sone:
Cool, dankeschön.
Leider verstehe ich den Code nicht ganz. Wenn bei
is_specializationArgumente mit T=O<int> und U=O reinkommen wie bei Deiner 3.static_assert-Zeile, wieso matcht er dann auf Deinenis_specialization_impl-true_type? Die Spezialisierung besagt dort doch beim ersten ArgumentU<V>, was hier dannO<O<int>>. Hm, nagut, das ist ja valide. Aber mit T=NoTemplate, U=O (erstestatic_assert-Zeile), würde die Spezialisierung jaO<NoTemplate>als erstes Argument erhalten, was dann genau so valide erscheint.Wäre super, wenn Du mich von meinen Irritationen/Denkfehlern befreien könntest.

camper:
Du meinst, bei fak<0> instanziiert er fak<-1>? Wieso führt das dann nicht zur Endlosrekursion?Zur Verkürzung: Stimmt, gar nicht bedacht, dass nur die TA-Typgleichheit gewährleistet sein muss, dankeschön.

Beste Grüße,
Eisflamme
-
Du meinst, bei fak<0> instanziiert er fak<-1>? Wieso führt das dann nicht zur Endlosrekursion?
Tut es ja gerade. Der Code ist nicht gültig. Es führt zu einer sehr großen Template-Instantiierungstiefe.
Wenn bei is_specialization Argumente mit T=O<int> und U=O reinkommen wie bei Deiner 3. static_assert-Zeile, wieso matcht er dann auf Deinen is_specialization_impl-true_type?
Weil es passt? T wird bei der partiellen Spezialisierung
intsein, und U=O. Daher matcht diese für alle Fälle, woU<T>übergeben wird - T wird dann zum Argument vonUdeduziert.Aber mit T=NoTemplate, U=O (erste static_assert-Zeile), würde die Spezialisierung ja O<NoTemplate> als erstes Argument erhalten
Nein! Denn T ist ja nicht
O<NoTemplate>! T ist NoTemplate, und daher passt das nicht.Du weißt doch, wie partielle Spezialisierungen funktionieren?

-
@fak: Okay

@Deine Lösung:
Hm, ich glaube, jetzt verstehe ich das, aber da ist der Compiler ja irgendwie schon ziemlich schlau?Gut, die partielle Spezialisierung heißt, dass er versucht auf einen U<T>-Typ zu matchen. Also muss U schon Mal O sein und T kann er sich dann angeln (deduzieren). Hm, hm... das ist logisch. Mein Denkfehler war, dass ich dachte, wenn U<T> erwartet wird und es ein T = NoTemplate gibt, dass er sich dann einfach selbst einen Typ U<NoTemplate> basteln könnte. Der darf aber nicht basteln, sondern soll nur matchen und wenn ihm innendrin was fehlt (also das Innere von U<.>), dann darf er sich das eben aus dem Template-Argument ziehen.
Wenn jemand Lust hat, kann er das ja nochmal umformulieren oder versuchen es anders zu pointieren, auf alle Fälle glaube ich es jetzt verstanden zu haben, danke.
