- Allgemeines
- Syntax
- „Folding über einem Komma”
- Ein Zeitvergleich: Iteration vs. Folding
- C++20: „Variadic Capture”
Ein Folding-Ausdruck ist ein arithmetischer Ausdruck, der einen Operator auf alle Werte eines variadischen Parameterpacks anwendet.
Beispiel:
Arithmetischer Ausdruck mit Plus-Operator – Notation ohne Folding:
int sum = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10;Berechnung desselben Ausdrucks durch einen Folding-Ausdruck:
auto add(auto ... args) {
return (... + args);
}
void test() {
int sum = add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}Folding-Audrücke lassen sich in 4 Kategorien einteilen, wie folgende Tabelle darstellt:
| Name | Ausdruck | Wird erweitert zu |
|---|---|---|
| Unary Left Fold | (... op pack) | ((pack1 op pack2) op ...) op packN |
| Binary Left Fold | (init op ... op pack) | (((init op pack1) op pack2) op ...) op packN |
| Unary Right Fold | (pack op ...) | pack1 op (... op (packN-1 op packN)) |
| Binary Right Fold | (pack op ... op init) | pack1 op (... op (packN-1 op (packN op init))) |
Tabelle 1: Folding-Audrücke.
Der Addierer und ein einfacher Printer demonstrieren in den Code-Snippets fold expressions.
Theoretisch stehen für Folding-Expressions 32 Operatoren zur Verfügung.
Diese wären +, -, *, /, %, ^, &, |, =, <, >, <<, >>, +=, -=, *=, /=, %=, ^=, &=, |=, <<=, >>=, ==, !=, <=, >=, &&, ||, ,, .* und ->*.
Ein Sonderfall des Foldings ist das sogenannte „Folding über einem Komma”. Dieses wird ebenfalls an mehreren Beispielen veranschaulicht:
template <typename... TArgs>
static void printerWithSeperator(TArgs ... args) {
std::string sep = " - ";
((std::cout << args << sep) , ...) << std::endl;
}Wir schließen dieses Kapitel mit einem Zeitvergleich ab. Um die ersten zehn natürlichen Zahlen zu addieren, betrachten wir zwei Funktionen:
auto add (auto ... values) {
return (... + values);
}und
auto add (auto ... values) {
auto list = { values ...};
auto sum{ 0 };
for (auto elem : list) {
sum += elem;
}
return sum;
}Wenn wir den Aufruf
auto sum { add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) };entsprechend oft wiederholen, zeichnet sich folgender Unterschied ab (Debug-Modus):
Elapsed time: 516 milliseconds. // folding
Elapsed time: 6211 milliseconds. // iterating
Ab C++20 lässt sich mit Hilfe des so genannten „Variadic Capture” (oft auch „Pack Expansion in Lambda Init-Capture” genannt) ein komplettes variadisches Parameterpaket (Parameter Pack) in einen Lambda-Ausdruck einbinden.
Diese Funktionalität ermöglicht es Ihnen, eine Liste von Argumenten „perfekt” an den Closure eines Lambda-Ausdrucks weiterzuleiten. Hierdurch kann das Schreiben generischer Wrapper oder Factory-Funktionen vereinfacht werden.
Beispiel:
01: template <typename... TArgs>
02: auto createDelayedPrinter(TArgs&&... args)
03: {
04: // Variadic Capture: [...args = std::forward<Args>(args)]
05: // This captures each element of the pack into the lambda.
06: return [...args = std::forward<TArgs>(args)] () {
07:
08: // using a fold expression to print the captured pack
09: (std::cout << ... << args) << std::endl;
10: };
11: }
12:
13: void test()
14: {
15: auto printMessage = createDelayedPrinter("Hello - ", "Variadic - ", "Capture!");
16:
17: // doing something else ...
18:
19: // now invoking the wrapper object, including the parameters
20: printMessage();
21: }Ausgabe:
Hello - Variadic - Capture!