================================================= Templates mit einer variablen Zahl von Parametern [TOC] ================================================= In der Template-Parameter-Deklaration kann nach der Typ-Angabe des Template-Parameters (wie etwa `typename` oder `unsigned int`) der Operator `...` verwendet werden. Dann gibt es beliebig viele Template-Parameter dieser Art, eventuell auch gar keinen. Folgende Template-Funktion akzeptiert beliebig viele Parameter beliebigen Typs: ---- CODE (type=cpp) ------------------------------------------------------- template void f(Args... args) { } ---------------------------------------------------------------------------- `Args` steht in diesem Fall nicht nur für einen Template-Parameter, sondern für beliebig viele. Dieser Template-Parameter kann mit dem Operator `...` expandiert werden. Eine Expansion erzeugt immer eine durch Kommata getrennte Liste aller Elemente, eventuell auch keinem einzigen. Bei der Parameter-Liste einer Template-Funktion führt dies zu einer entsprechenden Expansion und der Parametername `args` wird selbst zu einem Pack, der nicht nur für einen einzelnen Funktionsparameter steht, sondern für die gesamte (eventuell auch leere) Liste der Funktionsparameter. Ein solcher Funktions-Parameter-Pack lässt sich innerhalb der Funktion mit dem `...`-Operator expandieren. Am einfachsten wäre es natürlich, den Funktions-Parameter-Pack einfach weiterzugeben. Dies am besten mit _perfect forwarding_: ---- CODE (type=cpp) ------------------------------------------------------- template void g(Args... args) { } template void f(Args&&... args) { g(std::forward(args)...); } ---------------------------------------------------------------------------- Beachten Sie hier, dass `...` nicht hinter `args`, sondern hinter `std::forward(args)` steht. D.h. bei dem Aufruf ---- CODE (type=cpp) ------------------------------------------------------- f("hi", 3.14, 42); ---------------------------------------------------------------------------- entsteht innerhalb von _f_ daraus die Expansion ---- CODE (type=cpp) ------------------------------------------------------- void f(const char*&& arg0, double&& arg1, int&& arg2) { g(std::forward(arg0), std::forward(arg1), std::forward(arg2)); } ---------------------------------------------------------------------------- Wenn gewünscht lassen sich die einzelnen Parameter abarbeiten, indem rekursiv jeweils ein Parameter herausgegriffen wird. Je nachdem, ob eine leere Parameterliste akzeptabel ist oder nicht, kann die Rekursion mit gar keinen Parametern oder mit genau einem Parameter beendet werden. Hier wird erstere Variante gezeigt: ---- CODE (type=cpp) ------------------------------------------------------- void f() { // termination of recursion, nothing to be done } template void f(Arg arg, Args... args) { // process arg f(args...); } ---------------------------------------------------------------------------- Aufgaben ======== * Ergänzen Sie obiges Beispiel so, dass jedes Argument (auf jeweils einer eigenen Zeile) auf `std:cout` ausgegeben wird und testen Sie dies. * Entwickeln Sie eine rekursive Template-Funktion _update_ mit einem Funktionsobjekt und beliebig vielen _lvalue reference_-Parametern, die das Funktionsobjekt für jeden Parameter so aufruft, so dass der Parameter verändert werden kann. Beispiel für einen Aufruf, der die drei Variablen _s_, _x_ und _i_ einliest: ---- CODE (type=cpp) ------------------------------------------------------- std::string s; double x; int i; update([](auto& var) { std::cin >> var; }, s, x, i); ---------------------------------------------------------------------------- :navigate: up -> doc:index next -> doc:session09/page02