Menu
Coddy logo textTech

std::function et std::bind

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 86 sur 104.

Bien que les lambdas soient puissantes, vous avez parfois besoin de stocker des objets appelables de différents types de manière uniforme, ou d'adapter des fonctions existantes pour qu'elles correspondent à une signature requise. std::function et std::bind de l'en-tête <functional> résolvent ces problèmes.

std::function est un wrapper à effacement de type qui peut contenir n'importe quel élément appelable (callable) correspondant à une signature spécifique - fonctions, lambdas ou objets fonctions :

#include <iostream>
#include <functional>

int add(int a, int b) { return a + b; }

int main() {
    std::function<int(int, int)> operation;
    
    operation = add;                              // Fonction classique
    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 crée un nouvel objet appelable en fixant certains arguments d'une fonction existante. Utilisez std::placeholders::_1, _2, etc. pour marquer les arguments qui restent variables :

#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!
}

Ces outils sont particulièrement utiles en POO pour stocker des callbacks en tant que membres de classe ou pour implémenter le patron Strategy. Cependant, le C++ moderne préfère souvent les lambdas à std::bind pour une meilleure lisibilité et de meilleures performances - utilisez std::bind principalement lorsque vous devez réorganiser les arguments ou travailler avec du code existant.

challenge icon

Défi

Facile

Construisons une calculatrice configurable qui démontre la puissance de std::function et std::bind. Vous allez créer un système où les opérations mathématiques peuvent être stockées, échangées et personnalisées au moment de l'exécution, montrant ainsi comment ces outils permettent une gestion flexible des rappels (callbacks).

Vous organiserez votre code sur trois fichiers :

  • MathOperations.h : Définissez une collection de fonctions mathématiques autonomes qui serviront de bibliothèque d'opérations.

    Créez ces fonctions :

    • add(int a, int b) — retourne la somme
    • subtract(int a, int b) — retourne la différence (a - b)
    • multiply(int a, int b) — retourne le produit
    • power(int base, int exponent, int multiplier) — retourne multiplier * (base ^ exponent). Utilisez une boucle simple pour calculer la puissance (supposez des exposants non négatifs).
  • Calculator.h : Définissez une classe Calculator qui utilise std::function pour stocker et exécuter des opérations de manière dynamique.

    Votre calculatrice doit avoir :

    • Un membre privé std::function<int(int, int)> pour stocker l'opération binaire actuelle
    • setOperation(std::function<int(int, int)> op) — définit l'opération actuelle
    • calculate(int a, int b) — exécute l'opération stockée et retourne le résultat

    Incluez les en-têtes nécessaires (<functional>) et assurez-vous d'inclure votre en-tête MathOperations.

  • main.cpp : Lisez trois entrées :
    1. Nom de l'opération : add, subtract, multiply, ou square
    2. Premier nombre (entier)
    3. Deuxième nombre (entier)

    Créez un Calculator et configurez-le en fonction du nom de l'opération :

    • Pour add, subtract, et multiply : assignez la fonction correspondante directement à la calculatrice
    • Pour square : utilisez std::bind pour créer une version spécialisée de power qui utilise toujours l'exposant 2 et le multiplicateur 1. La fonction liée doit accepter deux arguments où seul le premier est utilisé comme base (le second argument peut être ignoré à l'aide d'un espace réservé/placeholder).

    Après avoir défini l'opération, appelez calculate() avec vos deux nombres et affichez :

    Result: [value]

    Démontrez ensuite l'échange d'opérations en assignant une lambda qui retourne a + b + 100 à la calculatrice, en l'exécutant avec les mêmes entrées, et en affichant :

    With bonus: [value]

Par exemple, avec les entrées add, 10, et 5 :

Result: 15
With bonus: 115

Avec les entrées multiply, 7, et 3 :

Result: 21
With bonus: 110

Avec les entrées square, 4, et 0 :

Result: 16
With bonus: 104

Ce défi montre comment std::function offre un moyen uniforme de stocker différents types d'appelables (fonctions régulières, fonctions liées et lambdas), tandis que std::bind vous permet d'adapter des fonctions existantes en fixant certains de leurs arguments.

Aide-mémoire

L'en-tête <functional> fournit std::function et std::bind pour travailler avec des objets appelables de manière flexible.

std::function est un wrapper à effacement de type (type-erased) qui peut stocker n'importe quel appelable correspondant à une signature spécifique :

#include <functional>

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

operation = add;                              // Fonction régulière
operation = [](int a, int b) { return a * b; }; // Lambda

std::bind crée un nouvel appelable en fixant certains arguments d'une fonction existante. Utilisez std::placeholders::_1, _2, etc. pour les arguments variables :

#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);  // Fixe le premier argument
sayHello("Alice");  // Hello, Alice!

auto swapped = std::bind(greet, _2, _1);  // Réordonne les arguments
swapped("Bob", "Hi");  // Hi, Bob!

Ces outils sont utiles pour stocker des rappels (callbacks) en tant que membres de classe ou pour implémenter des systèmes de rappel flexibles. Le C++ moderne préfère souvent les lambdas à std::bind pour une meilleure lisibilité.

Essayez vous-même

#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"

using namespace std;

int main() {
    // Lire les entrées
    string operation;
    int num1, num2;
    cin >> operation >> num1 >> num2;
    
    // Créer une instance de Calculator
    Calculator calc;
    
    // TODO: Configurer le calculateur en fonction du nom de l'opération
    // Pour "add", "subtract", "multiply" : assigner directement la fonction correspondante
    // Pour "square" : utiliser std::bind pour créer une version spécialisée de power
    //   qui utilise toujours l'exposant 2 et le multiplicateur 1
    //   Astuce : Utiliser std::placeholders::_1 pour l'argument de base
    
    if (operation == "add") {
        // Votre code ici
    } else if (operation == "subtract") {
        // Votre code ici
    } else if (operation == "multiply") {
        // Votre code ici
    } else if (operation == "square") {
        // Votre code ici - utiliser std::bind avec la fonction power
    }
    
    // TODO: Appeler calculate() et afficher le résultat
    // Format : "Result: [value]"
    
    // TODO: Démontrer l'échange d'opérations
    // Assigner une lambda qui retourne a + b + 100 au calculateur
    // Appeler calculate() à nouveau et afficher le résultat
    // Format : "With bonus: [value]"
    
    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet