Menu
Coddy logo textTech

std::function & std::bind

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 86 von 104.

Während Lambdas leistungsstark sind, müssen Sie manchmal aufrufbare Objekte mit unterschiedlichen Typen auf einheitliche Weise speichern oder bestehende Funktionen anpassen, um einer erforderlichen Signatur zu entsprechen. std::function und std::bind aus dem Header <functional> lösen diese Probleme.

std::function ist ein typlöschender Wrapper, der jedes aufrufbare Objekt (Callable) aufnehmen kann, das einer bestimmten Signatur entspricht – 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 erzeugt ein neues aufrufbares Objekt (Callable), indem einige Argumente einer bestehenden Funktion fixiert werden. Verwenden Sie std::placeholders::_1, _2 usw., um Argumente zu markieren, 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");  // Hello, Alice!
    
    auto swapped = std::bind(greet, _2, _1);
    swapped("Bob", "Hi");  // Hi, Bob!
}

Diese Tools sind in der OOP besonders nützlich, um Callbacks als Klassenmitglieder zu speichern oder das Strategy-Pattern zu implementieren. Modernes C++ bevorzugt jedoch oft Lambdas gegenüber std::bind für eine bessere Lesbarkeit und Performance – verwenden Sie std::bind hauptsächlich dann, wenn Sie Argumente umordnen müssen oder mit Legacy-Code arbeiten.

challenge icon

Aufgabe

Einfach

Lassen Sie uns einen konfigurierbaren Taschenrechner bauen, der die Leistungsfähigkeit von std::function und std::bind demonstriert. Sie werden ein System erstellen, in dem mathematische Operationen zur Laufzeit gespeichert, ausgetauscht und angepasst werden können – was zeigt, wie diese Werkzeuge ein flexibles Callback-Management ermöglichen.

Sie werden Ihren Code über drei Dateien organisieren:

  • MathOperations.h: Definieren Sie eine Sammlung von eigenständigen mathematischen Funktionen, die als Ihre Operationsbibliothek dienen werden.

    Erstellen Sie diese Funktionen:

    • add(int a, int b) — gibt die Summe zurück
    • subtract(int a, int b) — gibt die Differenz (a - b) zurück
    • multiply(int a, int b) — gibt das Produkt zurück
    • power(int base, int exponent, int multiplier) — gibt multiplier * (base ^ exponent) zurück. Verwenden Sie eine einfache Schleife, um die Potenz zu berechnen (nehmen Sie nicht-negative Exponenten an).
  • Calculator.h: Definieren Sie eine Calculator-Klasse, die std::function verwendet, um Operationen dynamisch zu speichern und auszuführen.

    Ihr Calculator sollte Folgendes haben:

    • Ein privates Mitglied std::function<int(int, int)>, um die aktuelle binäre Operation zu speichern
    • setOperation(std::function<int(int, int)> op) — setzt die aktuelle Operation
    • calculate(int a, int b) — führt die gespeicherte Operation aus und gibt das Ergebnis zurück

    Fügen Sie die erforderlichen Header (<functional>) ein und stellen Sie sicher, dass Sie Ihren MathOperations-Header einbinden.

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

    Erstellen Sie einen Calculator und konfigurieren Sie ihn basierend auf dem Operationsnamen:

    • Für add, subtract und multiply: Weisen Sie die entsprechende Funktion direkt dem Taschenrechner zu
    • Für square: Verwenden Sie 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 mit einem Platzhalter ignoriert werden).

    Nachdem Sie die Operation festgelegt haben, rufen Sie calculate() mit Ihren zwei Zahlen auf und geben Sie Folgendes aus:

    Result: [value]

    Demonstrieren Sie dann das Austauschen von Operationen, indem Sie dem Taschenrechner ein Lambda zuweisen, das a + b + 100 zurückgibt, es mit denselben Eingaben ausführen und Folgendes ausgeben:

    With bonus: [value]

Zum 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 Ihnen ermöglicht, bestehende Funktionen anzupassen, indem Sie einige ihrer Argumente festlegen.

Spickzettel

Der Header <functional> bietet std::function und std::bind für die flexible Arbeit mit aufrufbaren Objekten (Callable Objects).

std::function ist ein typsicherer Wrapper (Type-Erased Wrapper), der jedes aufrufbare Objekt speichern kann, das einer bestimmten Signatur entspricht:

#include <functional>

std::function<int(int, int)> operation;

operation = add;                              // Reguläre Funktion
operation = [](int a, int b) { return a * b; }; // Lambda

std::bind erstellt ein neues aufrufbares Objekt, indem einige Argumente einer bestehenden Funktion fixiert werden. Verwenden Sie std::placeholders::_1, _2 usw. für variable Argumente:

#include <functional>

void greet(const std::string& greeting, const std::string& name) {
    std::cout << greeting << ", " << name << "!\n";
}

using namespace std::placeholders;

auto sayHello = std::bind(greet, "Hello", _1);  // Erstes Argument fixieren
sayHello("Alice");  // Hello, Alice!

auto swapped = std::bind(greet, _2, _1);  // Argumente umordnen
swapped("Bob", "Hi");  // Hi, Bob!

Diese Werkzeuge sind nützlich, um Callbacks als Klassenmitglieder zu speichern oder flexible Callback-Systeme zu implementieren. Modernes C++ bevorzugt oft Lambdas gegenüber std::bind für eine bessere Lesbarkeit.

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: Den Taschenrechner basierend auf dem Operationsnamen konfigurieren
    // Für "add", "subtract", "multiply": Die entsprechende Funktion direkt zuweisen
    // Für "square": std::bind verwenden, um eine spezialisierte Version von power zu erstellen
    //   die immer den Exponenten 2 und den Multiplikator 1 verwendet
    //   Hinweis: std::placeholders::_1 für das Basis-Argument verwenden
    
    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 - std::bind mit der power-Funktion verwenden
    }
    
    // TODO: calculate() aufrufen und das Ergebnis ausgeben
    // Format: "Result: [value]"
    
    // TODO: Das Austauschen von Operationen demonstrieren
    // Einen Lambda-Ausdruck, der a + b + 100 zurückgibt, dem Taschenrechner zuweisen
    // calculate() erneut aufrufen und das Ergebnis ausgeben
    // 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