Programmation fonctionnelle en C++ : de A à Z
De nombreux développeurs, familiers avec Python ou JavaScript, sont habitués aux outils puissants de la programmation fonctionnelle : lambdas, map, filter, reduce. Mais saviez-vous que C++ offre des possibilités tout aussi flexibles pour le style fonctionnel ? Dans cet article, nous allons explorer la programmation fonctionnelle en C++, des concepts de base aux techniques avancées. Vous apprendrez à écrire un code plus concis, sûr et expressif en utilisant les fonctionnalités modernes de C++11/14/17.
1. Fondamentaux de l'approche fonctionnelle : expressions lambda et std::function
La programmation fonctionnelle en C++ commence avec les expressions lambda. Elles permettent de créer des fonctions anonymes directement au point d'utilisation. La syntaxe d'une lambda est simple :
// Lambda de baseauto sum = [](int a, int b) -> int { return a + b; };std::cout << sum(5, 3); // 8
// Lambda avec capture de variablesint multiplier = 3;auto multiply = [multiplier](int x) { return x * multiplier; };std::cout << multiply(7); // 21La caractéristique clé des lambdas est la capture du contexte. Vous pouvez capturer des variables par valeur ([=]), par référence ([&]) ou de manière combinée. C'est la base des fermetures, qui sont le fondement du style fonctionnel.
Pour stocker et transmettre les lambdas, on utilise std::function — une enveloppe polymorphe pour les objets appelables :
#include <functional>
std::function<int(int, int)> operation;
operation = [](int a, int b) { return a + b; };std::cout << operation(10, 20); // 30
operation = [](int a, int b) { return a * b; };std::cout << operation(10, 20); // 200Conseil : Utilisez auto pour les lambdas si vous n'avez pas besoin de les stocker comme type. std::function ajoute une légère surcharge, donc il est préférable de l'utiliser uniquement lorsque le polymorphisme est nécessaire.
2. Algorithmes STL : map, filter, reduce faits maison
En C++, il n'y a pas de fonctions intégrées map, filter, reduce comme en Python, mais la STL fournit des algorithmes puissants qui les remplacent. Examinons les principaux :
2.1. Map — transformation des éléments
Au lieu de map, utilisez std::transform :
#include <algorithm>#include <vector>
std::vector<int> numbers = {1, 2, 3, 4, 5};std::vector<int> squares(numbers.size());
std::transform(numbers.begin(), numbers.end(), squares.begin(), [](int n) { return n * n; });// squares : {1, 4, 9, 16, 25}2.2. Filter — filtrage par condition
L'équivalent de filter — std::copy_if ou std::remove_if (avec suppression ultérieure) :
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};std::vector<int> evens;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(evens), [](int n) { return n % 2 == 0; });// evens : {2, 4, 6}2.3. Reduce — réduction de collection
Pour reduce, utilisez std::accumulate depuis <numeric> :
#include <numeric>
std::vector<int> numbers = {1, 2, 3, 4, 5};int sum = std::accumulate(numbers.begin(), numbers.end(), 0, [](int acc, int n) { return acc + n; });// sum = 15Exemple avancé : Combinaison de map, filter et reduce pour le traitement de données :
std::vector<std::string> names = {"Alice", "Bob", "Charlie", "David"};// On obtient la somme des longueurs des noms commençant par 'A' ou 'C'int result = std::accumulate(names.begin(), names.end(), 0, [](int acc, const std::string& name) { if (name[0] == 'A' || name[0] == 'C') { return acc + name.length(); } return acc;