TMP: Permutationen von Template-Parametern generieren
-
Das ist ein TMP-Abkürzungs-Trick. std::identity ist äquivalent zu:
template <typename T> struct identity { typedef T type; };Da viele TMP-Algorithmen ihr Ergebnis durch ein typedef type herausgeben, lässt sich ein Algorithmus xyz
template <typename Foo> struct xyz { typedef Foo type; };kürzer schreiben als
template <typename Foo> struct xyz : identity<Foo> {};camper lässt nun seine typelist selbst von std::identity erben. Wenn er nun als Ergebnis eine Leere Typliste haben möchte, kann er deshalb nochmal abkürzen und statt
: identity<typelist<>>direkt
: typelist<>schreiben. Sein repeat_list macht davon Gebrauch.
-
Pseudo-Edit: Das "Leere" aus dem letzten Satz streichen. Geht natürlich mit allen Typlisten.
-
Ok, danke. Ich wusste vorher noch gar nicht, das man mehr als ein Variadic Template Argument haben kann.

-
Das geht auch nur bei Klassentemplate-Spezialisierungen und Funktionstemplates wo die Parameter deduziert werden können.
-
314159265358979__ schrieb:
Das geht auch nur bei Klassentemplate-Spezialisierungen und Funktionstemplates wo die Parameter deduziert werden können.
Ja, das habe ich mir auch schon gedacht. Sonst wäre das ja nicht mehr eindeutig.
-
Wow, super, vielen Dank. Jetzt mach ich mich mal ans Verstehen.

-
314159265358979__ schrieb:
Wenn er nun als Ergebnis eine Leere Typliste haben möchte, kann er deshalb nochmal abkürzen und statt
: identity<typelist<>>direkt
: typelist<>schreiben. Sein repeat_list macht davon Gebrauch.
Wenn ich das jetzt richtig verstehe, heißt das, dass eine Klasse von sich selbst ableiten kann? o.O
-
Hier die bereinigte Version.
#include <iostream> #include <string> #include <typeinfo> template <typename T, typename U> void foo() { std::cout << typeid(T).name() << '\t' << typeid(U).name() << '\n'; } template <typename T> struct identity { using type = T; }; template <typename... T> struct typelist : identity<typelist<T...>> {}; template <typename... L> struct concat_typelists : concat_typelists<typename L::type...> {}; template <typename... T, typename... U, typename... L> struct concat_typelists<typelist<T...>, typelist<U...>, L...> : concat_typelists<typelist<T..., U...>, L...> {}; template <typename... T> struct concat_typelists<typelist<T...>> : typelist<T...> {}; template <std::size_t N, typename L> struct repeat_list; template <std::size_t N, typename... T> struct repeat_list<N, typelist<T...>> : concat_typelists<typelist<T...>, repeat_list<N-1, typelist<T...>>> {}; template <typename... T> struct repeat_list<0, typelist<T...>> : typelist<> {}; template <std::size_t N, typename L> struct repeat_types : repeat_types<N, typename L::type> {}; template <std::size_t N, typename... T> struct repeat_types<N, typelist<T...>> : concat_typelists<repeat_list<N, typelist<T>>...> {}; template <typename... T, typename... U> void dispatch_helper(std::size_t i, typelist<T...>, typelist<U...>) { static void (*const foos[])() = { foo<T, U>... }; return foos[i](); } template <typename... T> void dispatch(std::size_t i, std::size_t j) { using types = typelist<T...>; using types_repeated = repeat_types<sizeof...(T), types>; using list_repeated = repeat_list<sizeof...(T), types>; dispatch_helper(i*sizeof...(T)+j, types_repeated(), list_repeated()); }; struct Foo {}; struct Bar {}; struct Baz {}; int main(int argc, char** argv) { if ( argc >= 2 ) dispatch<Foo,Bar,Baz>(std::stoul(argv[1]), std::stoul(argv[2])); }TMP-Code leidet häufig darunter, dass eine Menge syntaktischer Lärm erzeugt wird. Die Auswertung von Metafunktionen erfolgt üblicherweise durch die Verwendung des in der Funktion enthaltenen typedefs type. Innerhalb von templates führt das schnell zu typename Foo::type-Orgien.
Unter Beachtung zweier Regeln lässt sich das weitgehend vermeiden: die typenames ...::type stehen zwar noch da, aber nicht mehr an einer interessanten Stelle; sondern in der Definition des Primärtemplates, das andernfalls undefiniert bliebe.
1. Jedes Template, das eigentlich keine Funktion darstellt, erbt von identity, enhält also ein typedef auf sich selbst und ist damit eine Metafunktion, die sich selbst zurückgibt.
2. Jede andere Metafunktion erbt von ihrem Ergebnis.Auf diese Weise kann dann auch typename ::type an Stellen wie in Zeilen 50-53 vermieden werden. Der Funktionsaufruf funktioniert, weil hierfür eine besondere (und relativ selten gebrauchte) Deduktionsregel existiert, die es erlaubt, eine Basisklasse zu deduzieren, wenn die Deduktion andernfalls fehlschlagen würde.
std::identity ist leider wieder aus dem Standard entfernt worden

-
Hacker schrieb:
Wenn ich das jetzt richtig verstehe, heißt das, dass eine Klasse von sich selbst ableiten kann? o.O
Nein. Aber du darfst eine Klasse als Template-Parameter der Basisklasse verwenden. Das nennt man dann CRTP.
-
314159265358979__ schrieb:
Hacker schrieb:
Wenn ich das jetzt richtig verstehe, heißt das, dass eine Klasse von sich selbst ableiten kann? o.O
Nein. Aber du darfst eine Klasse als Template-Parameter der Basisklasse verwenden. Das nennt man dann CRTP.
Das kenn ich schon längst, aner hier meintest du doch es wär die vereinfachte Version.. die ohne CRTP.
Edit:
template <typename... T> struct typelist : std::identity<typelist<T...>> {};Das geht natürlich. Das ist CRTP. Das..
template <typename... T> struct typelist : typelist<>{};Geht doch nicht? Obwohl es natürlich verschiedene Klassen sind.. der Konstruktor bswp. bleibt gleich, oder? Und auch der Compiler spuckt entsprechende Fehlermeldungen aus (na nicht die, die ich grade meine, aber z. B. dass
typelistunvollständig ist)...