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 devez parfois stocker des objets appelables de types différents de manière uniforme, ou adapter des fonctions existantes afin 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 une enveloppe avec effacement de type qui peut contenir tout appelable correspondant à une signature spécifique : des fonctions, des lambdas ou des objets fonction :
#include <iostream>
#include <functional>
int add(int a, int b) { return a + b; }
int main() {
std::function<int(int, int)> operation;
operation = add; // Fonction régulière
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"); // Bonjour, Alice !
auto swapped = std::bind(greet, _2, _1);
swapped("Bob", "Hi"); // Salut, Bob !
}Ces outils sont particulièrement utiles en POO pour stocker des fonctions de rappel en tant que membres de classe ou pour implémenter le modèle Stratégie. Cependant, le C++ moderne privilégie souvent les lambdas plutôt que std::bind pour une meilleure lisibilité et de meilleures performances : utilisez principalement std::bind lorsque vous devez réordonner les arguments ou travailler avec du code ancien.
Défi
FacileConstruisons une calculatrice configurable qui démontre la puissance de std::function et de std::bind. Vous allez créer un système dans lequel les opérations mathématiques peuvent être stockées, remplacées et personnalisées à l’exécution, montrant ainsi comment ces outils permettent une gestion flexible des rappels.
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): renvoie la sommesubtract(int a, int b): renvoie la différence (a - b)multiply(int a, int b): renvoie le produitpower(int base, int exponent, int multiplier): renvoiemultiplier * (base ^ exponent). Utilisez une boucle simple pour calculer la puissance (en supposant que les exposants sont non négatifs).
Calculator.h: définissez une classeCalculatorqui utilisestd::functionpour stocker et exécuter dynamiquement des opérations.Votre Calculator doit comporter :
- 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 actuellecalculate(int a, int b): exécute l’opération stockée et renvoie le résultat
Incluez les en-têtes nécessaires (
<functional>) et veillez à inclure votre en-tête MathOperations.- Un membre privé
main.cpp: lisez trois entrées :- Nom de l’opération :
add,subtract,multiplyousquare - Premier nombre (entier)
- Deuxième nombre (entier)
Créez un
Calculatoret configurez-le en fonction du nom de l’opération :- Pour
add,subtractetmultiply: affectez directement la fonction correspondante à la calculatrice - Pour
square: utilisezstd::bindpour créer une version spécialisée depowerqui utilise toujours l’exposant 2 et le multiplicateur 1. La fonction liée doit accepter deux arguments, dont seul le premier est utilisé comme base (le deuxième argument peut être ignoré à l’aide d’un paramètre fictif).
Après avoir défini l’opération, appelez
calculate()avec vos deux nombres et affichez :Result: [value]Démontrez ensuite le remplacement des opérations en affectant à la calculatrice une lambda qui renvoie
a + b + 100, exécutez-la avec les mêmes entrées et affichez :With bonus: [value]- Nom de l’opération :
Par exemple, avec les entrées add, 10 et 5 :
Result: 15
With bonus: 115Avec les entrées multiply, 7 et 3 :
Result: 21
With bonus: 110Avec les entrées square, 4 et 0 :
Result: 16
With bonus: 104Ce défi montre comment std::function fournit une manière uniforme de stocker différents types d’objets appelables (fonctions ordinaires, fonctions liées et lambdas), tandis que std::bind vous permet d’adapter des fonctions existantes en fixant certains de leurs arguments.
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 la fonction correspondante directement
// 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 des opérations
// Assignez une lambda qui retourne a + b + 100 au calculateur
// Appelez calculate() à nouveau et affichez le résultat
// Format : "With bonus: [value]"
return 0;
}
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
1Fondamentaux de la POO
Fichiers externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d’accès en C++Spécificateurs d’accès en profondeurMasquage de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l’opérateur d’affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif – Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur générique12Fonctionnalités modernes de C++
Sémantique de déplacement et rvaluesTransfert parfaitExpressions lambda en profondeurstd::function et std::bindconstexpr et constevalLiaisons structuréesoptional, variant, anyEntraînez-vous par vous-même : Compilateur C++ en ligne