std::function & std::bind
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 86 von 104.
Obwohl Lambdas leistungsfähig sind, musst du manchmal aufrufbare Objekte mit unterschiedlichen Typen einheitlich speichern oder vorhandene Funktionen an eine erforderliche Signatur anpassen. std::function und std::bind aus dem Header <functional> lösen diese Probleme.
std::function ist ein typgelöschter Wrapper, der jedes aufrufbare Objekt mit einer bestimmten Signatur aufnehmen kann – Funktionen, Lambdas oder Funktionsobjekte:
#include <iostream>
#include <functional>
int add(int a, int b) { return a + b; }
int main() {
std::function<int(int, int)> operation;
operation = add; // Reguläre Funktion
std::cout << operation(3, 4) << "\n"; // 7
operation = [](int a, int b) { return a * b; }; // Lambda
std::cout << operation(3, 4) << "\n"; // 12
}std::bind erstellt ein neues aufrufbares Objekt, indem einige arguments einer vorhandenen Funktion festgelegt werden. Verwende std::placeholders::_1, _2 usw., um arguments zu kennzeichnen, die variabel bleiben:
#include <iostream>
#include <functional>
void greet(const std::string& greeting, const std::string& name) {
std::cout << greeting << ", " << name << "!\n";
}
int main() {
using namespace std::placeholders;
auto sayHello = std::bind(greet, "Hello", _1);
sayHello("Alice"); // Hallo, Alice!
auto swapped = std::bind(greet, _2, _1);
swapped("Bob", "Hi"); // Hi, Bob!
}Diese Werkzeuge sind in OOP besonders nützlich, um callback als Klassenmitglieder zu speichern oder das Strategy-Muster zu implementieren. Modernes C++ bevorzugt jedoch häufig Lambdas gegenüber std::bind, da diese besser lesbar und leistungsfähiger sind – verwende std::bind hauptsächlich, wenn du Argumente neu anordnen oder mit Legacy-Code arbeiten musst.
Aufgabe
EinfachErstelle einen konfigurierbaren Taschenrechner, der die Leistungsfähigkeit von std::function und std::bind demonstriert. Du erstellst ein System, in dem mathematische Operationen zur Laufzeit gespeichert, ausgetauscht und angepasst werden können, und zeigst damit, wie diese Werkzeuge eine flexible Verwaltung von Callbacks ermöglichen.
Du organisierst deinen Code über drei Dateien:
MathOperations.h: Definiere eine Sammlung eigenständiger mathematischer Funktionen, die als deine Operationsbibliothek dienen.Erstelle diese Funktionen:
add(int a, int b): gibt die Summe zurücksubtract(int a, int b): gibt die Differenz zurück (a - b)multiply(int a, int b): gibt das Produkt zurückpower(int base, int exponent, int multiplier): gibtmultiplier * (base ^ exponent)zurück. Verwende eine einfache Schleife, um die Potenz zu berechnen (nichtnegative Exponenten werden vorausgesetzt).
Calculator.h: Definiere eineCalculator-Klasse, diestd::functionverwendet, um Operationen dynamisch zu speichern und auszuführen.Dein Calculator sollte Folgendes enthalten:
- Ein privates Member
std::function<int(int, int)>zum Speichern der aktuellen binären Operation setOperation(std::function<int(int, int)> op): legt die aktuelle Operation festcalculate(int a, int b): führt die gespeicherte Operation aus und gibt das Ergebnis zurück
Füge die erforderlichen Header (
<functional>) ein und stelle sicher, dass du deinen MathOperations-Header einbindest.- Ein privates Member
main.cpp: Lies drei Eingaben ein:- Operationsname:
add,subtract,multiplyodersquare - Erste Zahl (Ganzzahl)
- Zweite Zahl (Ganzzahl)
Erstelle einen
Calculatorund konfiguriere ihn abhängig vom Operationsnamen:- Für
add,subtractundmultiply: Weise die entsprechende Funktion direkt dem Calculator zu - Für
square: Verwendestd::bind, um eine spezialisierte Version vonpowerzu erstellen, die immer den Exponenten 2 und den Multiplikator 1 verwendet. Die gebundene Funktion sollte zwei Argumente akzeptieren, wobei nur das erste als Basis verwendet wird (das zweite Argument kann mithilfe eines Platzhalters ignoriert werden).
Rufe nach dem Festlegen der Operation
calculate()mit deinen beiden Zahlen auf und gib Folgendes aus:Result: [value]Demonstriere anschließend das Austauschen von Operationen, indem du dem Calculator eine Lambda-Funktion zuweist, die
a + b + 100zurückgibt, sie mit denselben Eingaben ausführst und Folgendes ausgibst:With bonus: [value]- Operationsname:
Beispiel mit den Eingaben add, 10 und 5:
Result: 15
With bonus: 115Mit den Eingaben multiply, 7 und 3:
Result: 21
With bonus: 110Mit den Eingaben square, 4 und 0:
Result: 16
With bonus: 104Diese Herausforderung zeigt, wie std::function eine einheitliche Möglichkeit bietet, verschiedene aufrufbare Typen (reguläre Funktionen, gebundene Funktionen und Lambdas) zu speichern, während std::bind es dir ermöglicht, vorhandene Funktionen anzupassen, indem einige ihrer Argumente festgelegt werden.
Probier es selbst
#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"
using namespace std;
int main() {
// Eingaben lesen
string operation;
int num1, num2;
cin >> operation >> num1 >> num2;
// Eine Calculator-Instanz erstellen
Calculator calc;
// TODO: Konfiguriere den Calculator basierend auf dem Operationsnamen
// Für "add", "subtract", "multiply": weise die entsprechende Funktion direkt zu
// Für "square": verwende std::bind, um eine spezialisierte Version von power zu erstellen
// die immer den Exponenten 2 und den Multiplikator 1 verwendet
// Hinweis: Verwende std::placeholders::_1 für das Basis-Argument
if (operation == "add") {
// Dein Code hier
} else if (operation == "subtract") {
// Dein Code hier
} else if (operation == "multiply") {
// Dein Code hier
} else if (operation == "square") {
// Dein Code hier - verwende std::bind mit der power-Funktion
}
// TODO: Rufe calculate() auf und gib das Ergebnis aus
// Format: "Result: [value]"
// TODO: Demonstriere das Vertauschen von Operationen
// Weise dem Calculator eine Lambda zu, die a + b + 100 zurückgibt
// Rufe calculate() erneut auf und gib das Ergebnis aus
// Format: "With bonus: [value]"
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer Container12Moderne C++-Funktionen
Move-Semantik & RvaluesPerfektes ForwardingLambda-Ausdrücke im Detailstd::function & std::bindconstexpr und constevalStrukturierte Bindungenoptional, variant, anyÜbe selbstständig: Online-C++-Compiler