Foncteurs et expressions lambda
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 74 sur 104.
De nombreux algorithmes de la STL acceptent un objet appelable qui personnalise leur comportement. Vous avez déjà vu des lambdas utilisées avec std::transform. Explorons à la fois les foncteurs et les expressions lambda — deux façons de créer des objets appelables en C++.
Un foncteur (objet fonction) est une classe qui surcharge operator(), ce qui permet d’appeler ses instances comme des fonctions :
#include <algorithm>
#include <vector>
#include <iostream>
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
int main() {
std::vector<int> nums = {1, 2, 3, 4};
std::vector<int> result(nums.size());
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
// result: {3, 6, 9, 12}
}Chaque instance de foncteur peut conserver son propre état indépendant grâce aux variables membres définies lors de la construction — ce qu’un appel de fonction ordinaire ne peut pas faire. Cependant, définir une classe pour des opérations simples est verbeux. Les expressions lambda offrent une alternative concise :
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });La syntaxe des lambdas est [capture](parameters) { body }. La clause de capture spécifie quelles variables externes la lambda peut accéder. Utilisez [=] pour capturer toutes les variables par valeur, [&] pour toutes les capturer par référence, ou indiquez des variables spécifiques comme [factor] ou [&factor].
Les lambdas sont particulièrement utiles pour les opérations ponctuelles avec des algorithmes comme le tri selon des critères personnalisés :
std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });
// Trié par valeur absolue : {-1, -2, 5, 8}Défi
FacileConstruisons un calculateur de prix qui illustre à la fois les foncteurs et les expressions lambda pour appliquer différentes stratégies de remise aux prix des produits.
Vous organiserez votre code sur deux fichiers :
Discounts.h: définissez ici vos foncteurs de remise et vos fonctions utilitaires.Créez un foncteur appelé
PercentageDiscountqui stocke un pourcentage de remise (sous forme d'entier). Sonoperator()doit prendre un prix de typedoubleet retourner le prix remisé. Par exemple, une remise de 20 % sur 100 $ doit retourner 80 $.Créez un autre foncteur appelé
FixedDiscountqui stocke un montant fixe à soustraire (sous forme dedouble). Sonoperator()doit prendre un prix et retourner le prix moins le montant fixe (sans jamais être inférieur à 0).Créez une fonction appelée
printPricesqui prend unconst std::vector<double>&et affiche tous les prix séparés par des espaces, suivis d'un retour à la ligne. Formatez chaque prix avec deux décimales.main.cpp: lisez cinq valeurs (chacune sur une ligne distincte) :- Prix du premier produit (double)
- Prix du deuxième produit (double)
- Prix du troisième produit (double)
- Remise en pourcentage à appliquer (entier, par exemple 20 pour 20 %)
- Montant de la remise fixe (double)
Créez un vecteur contenant les trois prix et présentez les deux approches :
- Affichez
Original prices:suivi des prix - Utilisez
std::transformavec votre foncteurPercentageDiscountpour créer un nouveau vecteur de prix remisés. AffichezAfter percentage discount:suivi des résultats - Utilisez
std::transformavec votre foncteurFixedDiscountsur les prix d'origine pour créer un autre vecteur. AffichezAfter fixed discount:suivi des résultats - Utilisez
std::transformavec une expression lambda qui double chaque prix d'origine. AffichezPremium prices (doubled):suivi des résultats - Utilisez
std::sortavec une lambda pour trier les prix d'origine par ordre décroissant. AffichezSorted (high to low):suivi des prix triés
Par exemple, avec les valeurs 100.00, 50.00, 75.00, 20 et 15.00 :
Original prices: 100.00 50.00 75.00
After percentage discount: 80.00 40.00 60.00
After fixed discount: 85.00 35.00 60.00
Premium prices (doubled): 200.00 100.00 150.00
Sorted (high to low): 100.00 75.00 50.00 Ce défi vous permet de comparer les foncteurs (qui conservent un état tel que le montant de la remise) aux lambdas (qui capturent des variables pour effectuer rapidement des opérations en ligne). Les deux approches fonctionnent parfaitement avec les algorithmes STL tels que std::transform et std::sort.
Essayez vous-même
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// Lire les entrées
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// Créer un vecteur avec les trois prix
std::vector<double> prices = {price1, price2, price3};
// TODO: Print "Original prices:" followed by the prices using printPrices
// TODO: Use std::transform with PercentageDiscount functor
// Créer un nouveau vecteur pour les résultats
// Print "After percentage discount:" followed by the results
// TODO: Use std::transform with FixedDiscount functor on original prices
// Créer un nouveau vecteur pour les résultats
// Print "After fixed discount:" followed by the results
// TODO: Use std::transform with a lambda that doubles each original price
// Créer un nouveau vecteur pour les résultats
// Print "Premium prices (doubled):" followed by the results
// TODO: Use std::sort with a lambda to sort original prices in descending order
// Print "Sorted (high to low):" followed by the sorted prices
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ériqueEntraînez-vous par vous-même : Compilateur C++ en ligne