template-Conditional
-
Hallo.
Das gewöhnliche
std::conditional
fü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 analog
Mit "im Voraus definierte" meine ich, dass die Templates
T
undU
dieselbe Signaturen haben müssen wieConditional
und dassConditional
nur eine Signatur akzeptieren kann, wobei man daher für jeden Anwendungsfall ein eigenesConditional
schreiben (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 analog
Gibt 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 analog
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; }; };
-
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
UndApply
möchte / kann ich ja nicht schon in dermain
instanziieren, denn das ist die Aufgabe vom NutzerFoo
.Dass
Type
unpassend 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<...>::Type
zugreifen 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_constant
s automatisch Entpacken mitIsIntegralConstant
und einer wilden SFINAE-Konstruktion, aber man muss ja leider explizittypename
angeben, was man durch kein Mittel in eine Kondition packen kann.