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 und U dieselbe Signaturen haben müssen wie Conditional und dass Conditional nur eine Signatur akzeptieren kann, wobei man daher für jeden Anwendungsfall ein eigenes Conditional 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.


  • Mod

    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
    Und Apply möchte / kann ich ja nicht schon in der main instanziieren, denn das ist die Aufgabe vom Nutzer Foo .

    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 in Foo (und in allen anderen Nutzerklassen) auf typename T<...>::Type zugreifen statt direkt T<...> 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 mit IsIntegralConstant und einer wilden SFINAE-Konstruktion, aber man muss ja leider explizit typename angeben, was man durch kein Mittel in eine Kondition packen kann.


Log in to reply