Templates mit einer variablen Zahl von Parametern

Content

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:

template<typename... Args>
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:

template<typename... Args>
void g(Args... args) {
}

template<typename... Args>
void f(Args&&... args) {
   g(std::forward<Args>(args)...);
}

Beachten Sie hier, dass ... nicht hinter args, sondern hinter std::forward<Args>(args) steht. D.h. bei dem Aufruf

f("hi", 3.14, 42);

entsteht innerhalb von f daraus die Expansion

void f(const char*&& arg0, double&& arg1, int&& arg2) {
   g(std::forward<const char*>(arg0),
      std::forward<double>(arg1),
      std::forward<int>(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:

void f() {
   // termination of recursion, nothing to be done
}

template<typename Arg, typename... Args>
void f(Arg arg, Args... args) {
   // process arg
   f(args...);
}

Aufgaben