[C++0x] BaseTemplate<Args...> make_var(Args &&... in) - wie geht das?
-
Hallo,
Die im Titel bereits angedeutete Funktion soll vor allem Schreibaufwand sparen, indem Klassen, die ihre Template-Typen für ihre Konstruktorargumente verwenden, einfacher erstellt werden können.
Einfaches Beispiel:
template<typename T1, typename T2> class Printer { public: Printer(T1 t1, T2 t2) { std::cout << t1 << " " << t2 << "\n"; } }; int main() { auto var = make_var<Printer>(5, 6); }
Mein Versuch für make_var() sieht so aus:
template<template<typename... Args> class BaseTemplate, typename... Args> BaseTemplate<Args...> make_var(Args &&... in) { return BaseTemplate<Args...>(in...); }
error: no matching function for call to 'make_var(int, int)' error: unable to deduce 'auto' from '<expression error>'
Auch wenn ich BaseTemplate direkt durch Printer ersetze, kommen Fehler:
template<typename... Args> Printer<Args...> make_var(Args &&... in) { return Printer<Args...>(in...); }
error: wrong number of template arguments (1, should be 2) error: provided for 'template<class T1, class T2> class Printer'
Weiß jemand, wie man make_var implementieren kann?
mfg
wxSkip
-
Ich würde es so (ungetestet) machen:
template < template <typename...> class T, typename... Args > T<Args...> make_var(Args&&... args) { return T<Args...>(std::forward<Args>(args)...); }
-
314159265358979 schrieb:
Ich würde es so (ungetestet) machen:
template < template <typename...> class T, typename... Args > T<Args...> make_var(Args&&... args) { return T<Args...>(std::forward<Args>(args)...); }
Genau der gleiche Fehler, denn außer dem std::forward und dem nicht-Angeben von Args bei class T hast du ja auch nichts geändert.
-
Das ist ein Fall von GCC Bug #35722
Als workaround (dort zu finden) geht:#include <iostream> #include <utility> template<typename T1, typename T2> class Printer { public: Printer(T1 t1, T2 t2) { std::cout << t1 << " " << t2 << "\n"; } }; template<template <typename...> class T, typename... Args> struct Join { typedef T<Args...> type; }; template <template <typename...> class T, typename... Args> typename Join<T, Args...>::type make_var(Args&&... args) { return typename Join<T, Args...>::type(std::forward<Args>(args)...); } int main() { auto var = make_var<Printer>(5, 6); }
-
Man darf nicht vergessen, dass beim Muster Arg&& wobei Arg deduziert wird, für Arg ein Lvalue-Referenz-Typ rauskommen kann.
Wenn man bei Lvalues kein Printer<int&,int&>-Objekt bekommen möchte und bei konstanten Ausdrücken das const stört, sollte man folgendes schreiben:
Join<T, typename std::decay<Args>::type ... >::type
So ähnlich arbeitet zB auch make_tuple. Also,
int i = 3; auto x = make_tuple(i,3.1415);
macht aus x ein tuple<int,double> und kein tuple<int&,double> obwohl der entsprechende Templateparameter des make_tuple-Templates als int& deduziert wird; denn i ist ein Lvalue. Die Sonderbehandlung bei make_tuple bzgl reference_wrapper<> verschweige ich jetzt mal.
-
Danke camper und krümelkracker!
@krümelkracker: Wusste gar nicht, dass man T & && schreiben kann... Ich habe extra die rvalue-Referenz hingesetzt, um lvalue-Referenzen zu vermeiden.
@camper: Ich hatte sogar noch einen GCC-Bug mit variadic templates im Hinterkopf - wusste aber nicht mehr genau, worum es dabei ging.
-
Könnte man jetzt auch noch
make_var<Printer, true, false, 5>(1, 7)
schreiben, was dann einenPrinter<true, false, 5, int, int>(1,7)
zurückgibt?
template<typename... Types> Types... template_values
funktioniert schon einmal nicht.
-
wxSkip schrieb:
@krümelkracker: Wusste gar nicht, dass man T & && schreiben kann...
Kann man auch nicht -- also & und && direkt hintereinander im Quelltext.
Aber man kann T && schreiben, auch wenn T=int& gilt. Der Effekt: T&&=int& ("reference collapsing"). Ist ein & dabei, "drückt sich das durch". Sonst bleibt's &&.wxSkip schrieb:
Ich habe extra die rvalue-Referenz hingesetzt, um lvalue-Referenzen zu vermeiden.
Dann bist Du -- so wie viele andere -- in die komische-Perfect-Forwarding-erlaubende-Deduktionsregel-Falle gelaufen; denn T&& fängt alles, falls T deduziert wird -- sowohl Rvalues als auch Lvalues.
template<class T> void demo(T&&) {} int main() { int i = 42; demo(i); // --> T = int&, T&& = int& demo(6); // --> T = int , T&& = int&& }
-
wxSkip schrieb:
Könnte man jetzt auch noch
make_var<Printer, true, false, 5>(1, 7)
schreiben, was dann einenPrinter<true, false, 5, int, int>(1,7)
zurückgibt?
template<typename... Types> Types... template_values
funktioniert schon einmal nicht.Du müsstest für jede Anzahl an Compiletime-Argumenten eine Überladung schreiben, da man nicht mehr als 1 typename... in einem template haben kann.
-
314159265358979 schrieb:
wxSkip schrieb:
Könnte man jetzt auch noch
make_var<Printer, true, false, 5>(1, 7)
schreiben, was dann einenPrinter<true, false, 5, int, int>(1,7)
zurückgibt?
template<typename... Types> Types... template_values
funktioniert schon einmal nicht.Du müsstest für jede Anzahl an Compiletime-Argumenten eine Überladung schreiben, da man nicht mehr als 1 typename... in einem template haben kann.
Könnte man dann eine Ersatzlösung wie
make_var<Printer, ValueHolder<true, false, 5> >(1, 7)
verwenden? Konkret geht es mir um die Implementierung von ValueHolder bzw. wie kann ich es vermeidenTypeHolder<bool, bool, int>::ValueHolder<true, false, 5>
schreiben zu müssen?EDIT: Einfacher wäre wohl
ValueHolder<true, false, 5>::make_var<Printer>(1, 7)
. Das ändert allerdings nichts am Problem der impliziten Typerkennung bei Template-Wert-Argumenten.
-
Ich muss mich selbst korrigieren, nicht mal mit Überladung ist das lösbar. Zu deinem Edit: Muss ich mal nachdenken und evtl das heilige IRC befragen.
-
314159265358979 schrieb:
Ich muss mich selbst korrigieren, nicht mal mit Überladung ist das lösbar. Zu deinem Edit: Muss ich mal nachdenken und evtl das heilige IRC befragen.
Was ist denn das IRC? Wenn ich danach suche, finde ich bloß den hiesigen Foren-Channel...
-
Internet Relay Chat
-
Das zweite ist auch nicht möglich, aus dem selben Grund wie das erste - Du kannst kein template machen, das beliebige Compiletime-Werte schluckt.
-
314159265358979 schrieb:
Das zweite ist auch nicht möglich, aus dem selben Grund wie das erste - Du kannst kein template machen, das beliebige Compiletime-Werte schluckt.
Ich habe schon versucht, den zusätzlichen Schreibaufwand bei der TypeHolder-Variante durch ein Makro mit decltype() und einer Funktion, die den TypeHolder-Typ zurückliefert, zu umgehen, aber dann kam ich auf Fehler bei Join bei
typedef T<values..., Args...> type;
(der Bug) undtemplate<ValueTypes..., typename...> class T
(anderer Fehler), sodass ich es jetzt mit einer für mich verwendbaren Alternative (die IMHO beim Aufruf auch schöner aussieht) versuche:template<bool b1, int i1> class Holder { public: template<typename T1, typename T2> class Printer { public: Printer(T1 t1, T2 t2) { std::cout << b1 << " " << i1 << " " << t1 << " " << t2 << "\n"; } }; }; template<template<typename...> class T, typename... Args> struct Join { typedef T<Args...> type; }; template <template<typename...> class T, typename... Args> static typename Join<T, typename std::decay<Args>::type...>::type make_var(Args&&... args) { return typename Join<T, typename std::decay<Args>::type...>::type(std::forward<Args>(args)...); } int main() { auto var = make_var<Holder<true, 5>::Printer>(5, 6); }
-
Vielleicht wäre ja das hier etwas für dich:
template <bool b, int i, typename T0, typename T1> struct printer { printer(T0 t0, T1 t1) { std::cout << t0 << b << t1 << i; } }; template < template <bool, int, typename...> class T, bool B, int I, typename... Args > struct join { typedef T<B, I, Args...> type; }; template <bool B, int I, typename... Args> typename join<printer, B, I, typename std::decay<Args>::type...>::type make_printer(Args&&... args) { return typename join<printer, B, I, typename std::decay<Args>::type...>::type(std::forward<Args>(args)...); }
Der Aufruf sieht dann einfach so aus:
make_printer<true, 42>(3.14, 666);
Hier sind allerdings die ersten beiden Template-Parameter immer bool und int.
-
314159265358979 schrieb:
Vielleicht wäre ja das hier etwas für dich:
template <bool b, int i, typename T0, typename T1> struct printer { printer(T0 t0, T1 t1) { std::cout << t0 << b << t1 << i; } }; template < template <bool, int, typename...> class T, bool B, int I, typename... Args > struct join { typedef T<B, I, Args...> type; }; template <bool B, int I, typename... Args> typename join<printer, B, I, typename std::decay<Args>::type...>::type make_printer(Args&&... args) { return typename join<printer, B, I, typename std::decay<Args>::type...>::type(std::forward<Args>(args)...); }
Der Aufruf sieht dann einfach so aus:
make_printer<true, 42>(3.14, 666);
Hier sind allerdings die ersten beiden Template-Parameter immer bool und int.
Danke, aber ich brauche es dynamisch (sowohl für das Basis-Template als auch für die Werte).
-
Muss denn die Anzahl an möglichen Compile-Time Argumenten auch variabel sein oder sind es immer nur 2?
-
wxSkip schrieb:
Danke, aber ich brauche es dynamisch (sowohl für das Basis-Template als auch für die Werte).
Wie dynamisch? Wenn B und I nicht beim Compilieren bekannt sind, können sie keine Templateparameter sein.
-
camper schrieb:
wxSkip schrieb:
Danke, aber ich brauche es dynamisch (sowohl für das Basis-Template als auch für die Werte).
Wie dynamisch? Wenn B und I nicht beim Compilieren bekannt sind, können sie keine Templateparameter sein.
Ich meinte, die Anzahl und die Typen sollen dynamisch, also Compiletime-Abhängig und nicht hardgecoded sein.