| 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.
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.
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?
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
minimummit einigen Testbeispielen. - Schreiben Sie eine zweite Funktion
maximum, die das Maximum aller Parameter bestimmt.
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.
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
forLoop - Folding
- „Folding over a Comma”
- Lambda-Funktionen
- Self-Invoking Lambda-Funktionen (IIFE - Immediately Invoked Functional Expression)