template-Conditional
-
Hallo.
Das gewöhnliche
std::conditionalfür Typen dürfte bekannt sein. Für Werte gibt es den tenären?:-Operator. Wie löst man so etwas für Templates? In C++11 fällt mir nur eine Lösung für im Voraus definierte Template-Signaturen ein:template<bool Condition, template<int, int> class T, template<int, int> class U> struct Conditional { template<int A, int B> using Type = T<A, B>; }; // false-Case analogMit "im Voraus definierte" meine ich, dass die Templates
TundUdieselbe Signaturen haben müssen wieConditionalund dassConditionalnur eine Signatur akzeptieren kann, wobei man daher für jeden Anwendungsfall ein eigenesConditionalschreiben (oder von einem Makro generieren lassen) muss.In C++03 (was ich eigentlich wollte) fällt mir gar keine befriedigende Lösung ein. Das, was noch am nächsten kommt, ist dies hier:
template<bool Condition, template<int, int> class T, template<int, int> class U> struct Conditional { template<int A, int B> struct Type : public T<A, B> { }; }; // false-Case analogGibt es für den spezifischen Fall eine gute C++03-Lösung und gibt es für eine Verallgemeinerung C++03- / C++11-Lösungen? Variadic Templates sind keine allgemeine Lösung, da Templates auch andere Templates oder, wie auch bei mir im tatsächlichen Code, Werte annehmen können.
Gruss.
-
asfdlol schrieb:
Wie löst man so etwas für Templates?
Allgemein nicht.
asfdlol schrieb:
In C++03 (was ich eigentlich wollte) fällt mir gar keine befriedigende Lösung ein. Das, was noch am nächsten kommt, ist dies hier:
template<bool Condition, template<int, int> class T, template<int, int> class U> struct Conditional { template<int A, int B> struct Type : public T<A, B> { }; }; // false-Case analoguhm, typedef? Type ist möglicherweise auch nicht die beste Wahl des Bezeichners, schliesslich handelt es sich ja nicht um einen Typen.
template<bool Condition, template<int, int> class T, template<int, int> class U> struct Conditional { template<int A, int B> struct Apply { typedef T<A,B> Type; }; };
-
Theoretisch sowas:
http://ideone.com/hKE2GU#include <iostream> #include <vector> #include <list> template <bool C, template <typename...> class A, template <typename...> class B> struct If { template <typename... AArgs> using Type = A<AArgs...>; }; template <template <typename...> class A, template <typename...> class B> struct If<false, A, B> { template <typename... BArgs> using Type = B<BArgs...>; }; int main() { If<true, std::vector, std::list>::Type<int> ct = { 23, 42 }; If<false, std::vector, std::list>::Type<double> cf = { 3.14159, 13.37 }; for (auto i : ct) std::cout << i << "\n"; for (auto d : cf) std::cout << d << "\n"; return 0; }Ich vermute aber, dass das mit Value-Templates (ala template <int N>) nicht funktionieren wird, kann mich aber irren.
EDIT: also ich habe es gerade nochmal mit std::array<typename Type, size_t Size> probiert und meine Vermutung hat sich bestätigt.
-
camper schrieb:
uhm, typedef? Type ist möglicherweise auch nicht die beste Wahl des Bezeichners, schliesslich handelt es sich ja nicht um einen Typen.
template<bool Condition, template<int, int> class T, template<int, int> class U> struct Conditional { template<int A, int B> struct Apply { typedef T<A,B> Type; }; };Das erzeugt nicht das Verhalten, das ich erreichen möchte. Auf diese Weise kann ich das ausgewählte Template nicht weiterreichen:
#include <iostream> template<int, int> struct G1 { G1() { std::cout << "G1\n"; } }; template<int, int> struct G2 { G2() { std::cout << "G2\n"; } }; template<template<int, int> class T> struct Foo { T<7, 42> M; }; template<bool Condition, template<int, int> class T, template<int, int> class U> struct Conditional_asfdlol { template<int A, int B> struct Type : public T<A, B> { }; }; template<bool Condition, template<int, int> class T, template<int, int> class U> struct Conditional_camper { template<int A, int B> struct Apply { typedef U<A,B> Type; }; }; // Edit: Die false-Fälle braucht es in der Demonstration hier nicht. Habe es hardgecoded. int main() { Foo<G1> F1; Foo<G2> F2; Foo<Conditional_asfdlol<true, G1, G2>::Type> F3; Foo<Conditional_camper<false, G1, G2>::Apply> F4; // Nicht das gewünschte Ergebnis }http://ideone.com/LAV7GW
UndApplymöchte / kann ich ja nicht schon in dermaininstanziieren, denn das ist die Aufgabe vom NutzerFoo.Dass
Typeunpassend gewählt wurde, stimmt.@DrakoXP
Ja, an sowas dachte ich zuerst auch (siehe OP), aber gerade in meinem tatsächlichen Anwendungsfall habe ich es schon mit Nichttyp-Template-Parametern zu tun.Edit: Deine Version (camper) würde in der Tat in C++03 gleich viel bieten wie die C++11-
using-Lösung. Dazu müsste ich jedoch inFoo(und in allen anderen Nutzerklassen) auftypename T<...>::Typezugreifen statt direktT<...>zu nutzen. Ich werde es mir überlegen.Edit: Unsinn entfernt.
-
asfdlol schrieb:
@DrakoXP
Ja, an sowas dachte ich zuerst auch (siehe OP), aber gerade in meinem tatsächlichen Anwendungsfall habe ich es schon mit Nichttyp-Template-Parametern zu tun.Du wirst dir das wahrscheinlich denken können, aber als Workaround kann man,
sofern der Typ aller Wert-Template-Parameter gleich ist, diesen auch als T... (z.B. int...) schreiben.
-
DrakoXP schrieb:
Du wirst dir das wahrscheinlich denken können, aber als Workaround kann man,
sofern der Typ aller Wert-Template-Parameter gleich ist, diesen auch als T... (z.B. int...) schreiben.Nein, wusste ich nicht. Ich habe keine Ahnung von C++11. Aber danke für's Aufklären.
Ein weiterer (umständlicher und hässlicher) Workaround für C++11 wäre sowas hier: http://ideone.com/KNzR3d
#include <type_traits> #include <array> #include <iostream> template<bool Condition, template<typename...> class T, template<typename...> class U> struct Conditional { template<typename... Args> using Type = T<Args...>; }; template<template<typename...> class T, template<typename...> class U> struct Conditional<false, T, U> { template<typename... Args> using Type = U<Args...>; }; template<typename T, typename Size> using Array = std::array<T, Size::value>; template<typename T, typename Size> using BigArray = std::array<T, Size::value * 2>; int main() { Conditional<true, Array, BigArray>::Type<int, std::integral_constant<std::size_t, 42>> Foo; std::cout << Foo.size() << '\n'; Conditional<false, Array, BigArray>::Type<int, std::integral_constant<std::size_t, 42>> Bar; std::cout << Bar.size() << '\n'; }Zunächst wollte ich die
integral_constants automatisch Entpacken mitIsIntegralConstantund einer wilden SFINAE-Konstruktion, aber man muss ja leider explizittypenameangeben, was man durch kein Mittel in eine Kondition packen kann.