Skip to content

Latest commit

 

History

History
206 lines (149 loc) · 6.21 KB

File metadata and controls

206 lines (149 loc) · 6.21 KB

Aufgaben zu Folding

Zurück


Lösungen


Überblick

Aufgabe Beschreibung
Aufgabe 1 Logische Operationen mit beliebig vielen Operanden
Aufgabe 2 decltype und Type-Traits am Beispiel von sameType
(Voraussetzungen: std::is_same)
Aufgabe 3 „Folding over a Comma” am Beispiel zweier Funktionen minimum und maximum
(Voraussetzungen: Lambdas)
Aufgabe 4 Beliebig viele Werte zu einem std::vector-Objekt hinzufügen
Aufgabe 5 Störende Trennzeichen am Ende eines Folding-Ausdrucks

Tabelle 1: Aufgaben zu Folding.


Aufgabe 1: Logische Operationen mit beliebig vielen Operanden

Die Aufgabenstellung ist identisch zur ersten Aufgabe „Logische Operationen mit beliebig vielen Operanden” aus dem Abschnitt Variadische Templates: Setzen Sie zur Lösung dieses Mal Folding Expressions ein.


Aufgabe 2: Folding Expressions, decltype und Type-Traits am Beispiel von sameType

Voraussetzungen: std::is_same

Die Aufgabenstellung ist identisch zur zweiten Aufgabe „Logische Operationen mit beliebig vielen Operanden” aus dem Abschnitt Variadische Templates(ohne Zusatzaufgabe): Setzen Sie zur Lösung dieses Mal Folding Expressions ein.

Zusatzfrage: Welchen prinzipiellen Unterschied beobachten Sie im Lösungsansatz von Aufgabe 1 und 2?


Aufgabe 3: „Folding over a Comma” am Beispiel zweier Funktionen minimum und maximum

Voraussetzungen: Lambdas

Der Zugriff auf die einzelnen Werte eines Parameter Packs ist nicht ganz einfach. Eine übliche Vorgehensweise ist das so genannte „Folding over a Comma”. Hierbei handelt es sich um einen Folding-Ausdruck, der mit dem Komma-Operator – gebildet wird. Der Komma-Operator bewirkt in diesem Fall einzig und allein, dass alle Werte des Parameter Packs sequentiell aufgelistet werden.

Betrachten Sie das nachfolgende Beispiel einer Implementierung der Funktion minimum: Die Funktion kann eine variabel lange Liste von Parametern (mindestens zwei) entgegennehmen und liefert den minimalen Wert zurück. Erklären Sie die Funktionsweise ihrer Realisierung. Welche Rolle spielt Zeile 14?

template <typename T, typename ...  TArgs>
auto minimum(const T& x, const T& y, const TArgs&... args)
{
    auto m{ (x < y) ? x : y };

    if constexpr (sizeof... (args) > 0)
    {
        auto helper = [&] (const auto& value) {
            if (value < m) {
                m = value;
            }
        };

        ( ... , helper(args) );
    }

    return m;
}
  • Testen Sie die Korrektheit der Funktion minimum mit einigen Testbeispielen.
  • Schreiben Sie eine zweite Funktion maximum, die das Maximum aller Parameter bestimmt.

Aufgabe 4: Beliebig viele Werte zu einem std::vector-Objekt hinzufügen

Sowohl mit variadischen Templates als auch mit Folding lässt sich ein elegantes Funktionstemplate pushBackAll<T> schreiben, das eine beliebige Anzahl von Parametern mit push_back einem std::vector-Objekt hinzufügt. Da diese Aufgabe in den Übungen zum Thema Folding enthalten ist, realisieren Sie das Funktionstemplate mit einem Folding Ausdruck.

Das folgende – hoffentlich intuitive – Beispiel sollte ausführbar sein:

#include <iostream>
#include <vector>

template <typename T, typename ...  TArgs>
void pushBackAll(std::vector<T>& vec, const TArgs&... args)
{
    (vec.push_back(args) , ...);
}

int main()
{
    std::vector<double> values;
    pushBackAll<double>(values, 30.0, 31.0, 32.0);
    for (auto elem : values) {
        std::cout << elem << ' ';
    }
    std::cout << std::endl;
}

Ausgabe:

30 31 32

Zusatzaufgabe: Man könnte das Funktionstemplate pushBackAll<T> auch mit variadischen Templates oder auch dem Trick mit einer Initialisierungsliste und wiederum Folding (dieses Mal „über einem Komma”) realisieren. Implementieren Sie die weiteren Varianten.


Aufgabe 5: Störende Trennzeichen am Ende eines Folding-Ausdrucks

Betrachten Sie die nachfolgende Funktion printer. Mit Hilfe von variadischen Templates kann die Funktion eine beliebige Anzahl von Parametern entgegennehmen und diese mit Hilfe eines Folding-Ausdrucks auf der Konsole ausgeben:

template <typename ...ARGS>
void printer(ARGS ...args) {
    (std::cout << ... << args);
    std::cout << std::endl;
}

Das Folding findet hier mit dem <<-Operator statt (genauer: Binary Left Folding), die Argumente werden ohne jegliches Trennzeichen in das std::cout-Objekt geschoben. Eine Anweisung der Gestalt printer(1, "ABC", 2, "DEF", 3, "GHI"); führt zur Ausgabe

1ABC2DEF3GHI

Möchte man die einzelnen Argumente von printer mit Trennzeichen auszugeben, dürfen wir das Folding nicht mit dem <<-Operator durchführen. Das so genannte „Folding over a Comma„ hilft jetzt weiter, es kommt ein Unary Right Folding–Ausdruck zum Einsatz:

template <typename ...ARGS>
void printer(ARGS ...args) {
    ((std::cout << args << ", ") , ...);
    std::cout << std::endl;
}

Ausgabe:

1, ABC, 2, DEF, 3, GHI,

bei Ausführung der Anweisung

printer(1, "ABC", 2, "DEF", 3, "GHI");

Wenn Sie die letzte Ausgabe genau betrachten, können Sie ein störendes Trennzeichen am Zeilenende beobachten.

Aufgabe: Schreiben Sie Funktion printer so um, dass das letzte Trennzeichen nicht mehr ausgegeben wird!

Zusatzfrage: In der Realisierung der printer-Funktion gibt es mehrere Variationsmöglichkeiten. Wenn Sie Ihre Modern C++ Kenntnisse vertiefen möchten, lassen sich zum Zwecke des Übens folgende Sprachkonzepte in eine Realisierung mit einbeziehen:

  • Initialisierungs-Liste (std::initializer_list) – Achtung: Hier gibt es eine Einschränkung bzgl. der Datentypen der Parameter beim Aufruf.
  • Range-based for Loop
  • Folding
  • Folding over a Comma”
  • Lambda-Funktionen
  • Self-Invoking Lambda-Funktionen (IIFE - Immediately Invoked Functional Expression)

Lösungen


Zurück