In C++ bezeichnet man “Generics” üblicherweise als Vorlagen oder auf Englisch “Templates”. Templates ermöglichen es, generischen Code zu schreiben, der mit unterschiedlichen Datentypen funktioniert, ohne dass der Code für jeden einzelnen Typ neu geschrieben werden muss.
Ein einfaches Beispiel für eine generische Klasse wäre eine Klasse für ein Paar von Werten:
template <typename T>
class Pair {
private:
T first;
T second;
public:
Pair(T a, T b) : first(a), second(b) {}
T getFirst() { return first; }
T getSecond() { return second; }
};
// Verwendung:
Pair<int> intPair(1, 2);
Pair<std::string> stringPair("Hello", "World");Man kann auch generische Funktionen erstellen:
template <typename T>
T getMax(T a, T b) {
return (a > b) ? a : b;
}
// Verwendung:
int a = getMax(3, 4); // gibt 4 zurück
double b = getMax(3.5, 4.5); // gibt 4.5 zurückMan kann mehrere Template-Parameter haben:
template <typename T, typename U>
class Pair {
private:
T first;
U second;
public:
Pair(T a, U b) : first(a), second(b) {}
T getFirst() { return first; }
U getSecond() { return second; }
};
Pair<int, std::string> mixedPair(1, "One");Man kann auch spezialisierte Implementierungen für bestimmte Typen bereitstellen:
template <>
class Pair<bool> {
// Spezialisierte Implementierung für den Typ bool
};Kompilierungsfehler: Fehler in Template-Code können schwer zu verstehen sein und zu langen Fehlermeldungen führen.
Code-Aufblähung: Jede Instanziierung eines Templates kann eine separate Kopie des Codes erzeugen. Compiler verwenden jedoch oft Techniken wie “Template-Instantiation”, um dies zu minimieren.
Funktions- und Klassen-Templates sind nicht immer austauschbar: Manchmal ist es nicht möglich, einen generischen Code mit Funktions-Templates zu schreiben, der mit Klassenvorlagen funktioniert, und umgekehrt.
Templates in C++ bieten eine mächtige Möglichkeit, generischen Code zu schreiben, der mit einer Vielzahl von Datentypen arbeiten kann. Sie sind das Rückgrat von vielen C++-Standardbibliothekskomponenten, wie den Containerklassen und Algorithmen. Trotz ihrer Komplexität ermöglichen sie eine hohe Wiederverwendbarkeit und Typsicherheit.
Lambda-Funktionen, häufig einfach als Lambdas bezeichnet, sind anonyme Funktionen, die direkt innerhalb des Körpers einer umgebenden Funktion definiert werden können. Sie wurden mit C++11 eingeführt und ermöglichen eine kurze und ausdrucksstarke Art, Funktionsobjekte zu schreiben.
Eine Lambda-Funktion in C++ hat die folgende allgemeine Form:
[capture](parameterliste) -> Rückgabetyp { Funktionskörper }Beispiele:
auto lambda1 = []() { std::cout << "Hello, World!" << std::endl; };
lambda1(); // Ausgabe: Hello, World!
auto lambda2 = [](int x, int y) -> int { return x + y; };
int sum = lambda2(3, 4); // sum hat den Wert 7Mit der Capture-Klausel können Sie Variablen aus dem umgebenden Gültigkeitsbereich “einfangen” und in der Lambda verwenden:
[=]: Einfangen aller verwendeten Variablen per
Wert.[&]: Einfangen aller verwendeten Variablen per
Referenz.[a, &b]: Einfangen von a per Wert und
b per Referenz.Beispiel:
int a = 5, b = 7;
auto lambda3 = [a, &b]() { std::cout << a << " " << ++b << std::endl; };
lambda3(); // Ausgabe: 5 8Mehrzeilige Lambdas: Die Körperfunktion kann mehrere Anweisungen umfassen.
auto printDetails = [](std::string name, int age) {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
};
printDetails("Alice", 30);Rückgabewerttyp-Inferezenz: Wenn der Rückgabetyp nicht explizit angegeben ist, wird er vom Compiler basierend auf dem Rückgabewert der Lambda abgeleitet.
auto lambda4 = [](int x, int y) { return x + y; };Generische Lambdas (ab C++14): Mit automatischer Typdeduktion können Sie generische Lambdas schreiben.
auto genericLambda = [](auto x, auto y) { return x + y; };Lambdas mit Zustand (ab C++14): Mit der
Verwendung von mutable können Lambdas, die per Wert
einfangen, Zustände ändern.
int z = 0;
auto increment = [z]() mutable { return ++z; };Lambdas sind besonders nützlich in Verbindung mit Algorithmen aus der C++ Standardbibliothek, da sie eine kompakte Möglichkeit bieten, benutzerdefinierte Operationen direkt an Ort und Stelle zu definieren.
Beispiel:
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(vec.begin(), vec.end(), [](int n) { std::cout << n * n << " "; }); // Ausgabe der QuadrateLambda-Funktionen in C++ sind eine mächtige Ergänzung zur Sprache und ermöglichen eine kurze, ausdrucksstarke und funktionale Programmierweise. Es ist wichtig, die Capture-Klausel und den Lebenszyklus der eingefangenen Variablen zu verstehen, um häufige Fehler zu vermeiden.