Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Erstelle 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ück
    • subtract(int a, int b): gibt die Differenz zurück (a - b)
    • multiply(int a, int b): gibt das Produkt zurück
    • power(int base, int exponent, int multiplier): gibt multiplier * (base ^ exponent) zurück. Verwende eine einfache Schleife, um die Potenz zu berechnen (nichtnegative Exponenten werden vorausgesetzt).
  • Calculator.h: Definiere eine Calculator-Klasse, die std::function verwendet, 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 fest
    • calculate(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.

  • main.cpp: Lies drei Eingaben ein:
    1. Operationsname: add, subtract, multiply oder square
    2. Erste Zahl (Ganzzahl)
    3. Zweite Zahl (Ganzzahl)

    Erstelle einen Calculator und konfiguriere ihn abhängig vom Operationsnamen:

    • Für add, subtract und multiply: Weise die entsprechende Funktion direkt dem Calculator 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. 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 + 100 zurückgibt, sie mit denselben Eingaben ausführst und Folgendes ausgibst:

    With bonus: [value]

Beispiel mit den Eingaben add, 10 und 5:

Result: 15
With bonus: 115

Mit den Eingaben multiply, 7 und 3:

Result: 21
With bonus: 110

Mit den Eingaben square, 4 und 0:

Result: 16
With bonus: 104

Diese 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;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C++-Compiler