Skip to content

Latest commit

 

History

History
174 lines (122 loc) · 4.31 KB

File metadata and controls

174 lines (122 loc) · 4.31 KB

Folding-Ausdrücke

Zurück


Quellcode


Inhalt


Allgemeines

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);
}

Syntax

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 ->*.


„Folding über einem Komma”

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;
}

Ein Zeitvergleich: Iteration vs. Folding

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

C++20: „Variadic Capture

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!

Zurück