Les fonctions avancées en C++ : surcharge, paramètres par défaut et lambdas

Les fonctions avancées en C++ : surcharge, paramètres par défaut et lambdas

Amine Abidi - Lead Software Engineer C++/Qt - Associé PointerLab

Publié par Amine Abidi - Lead Software Engineer C++/Qt - Associé PointerLab

Introduction

Les fonctions sont au cœur de la programmation en C++. En plus des fonctions classiques, le langage offre des fonctionnalités avancées comme la surcharge de fonctions, les paramètres par défaut, les fonctions inline et les fonctions lambda. Ces outils permettent d'écrire un code plus flexible, optimisé et lisible.

Avant de plonger dans ces fonctionnalités avancées, prenez le temps de revoir les bases des fonctions en C++


1. Surcharge de fonctions

La surcharge de fonctions consiste à définir plusieurs fonctions ayant le même nom, mais des signatures différentes (type ou nombre de paramètres). Le compilateur choisit automatiquement la version appropriée en fonction des arguments fournis.

Exemple :

#include <iostream>
using namespace std;

// Surcharge de fonctions
void afficher(int x) {
    cout << "Entier : " << x << endl;
}

void afficher(double x) {
    cout << "Double : " << x << endl;
}

void afficher(string x) {
    cout << "Chaîne : " << x << endl;
}

int main() {
    afficher(10);        // Appelle la version avec un entier
    afficher(3.14);      // Appelle la version avec un double
    afficher("C++");     // Appelle la version avec une chaîne
    return 0;
}

Points clés

  • Le nom de la fonction reste le même, mais les paramètres diffèrent.
  • La surcharge améliore la lisibilité du code et permet d'utiliser le même nom pour des opérations similaires.
  • Elle ne fonctionne pas uniquement sur le type de retour : seule la signature (type et nombre d’arguments) importe.

2. Paramètres par défaut

Les paramètres par défaut permettent de donner une valeur par défaut à un argument si celui-ci n'est pas fourni lors de l'appel de la fonction.

Exemple en C++

#include <iostream>
using namespace std;

void saluer(string nom = "Invité", int age = 0) {
    cout << "Bonjour, " << nom;
    if (age > 0) {
        cout << ", vous avez " << age << " ans.";
    }
    cout << endl;
}

int main() {
    saluer();                  // Utilise les valeurs par défaut
    saluer("Alice");           // Fournit uniquement le nom
    saluer("Bob", 25);         // Fournit le nom et l'âge
    return 0;
}

Points clés

  • Les paramètres par défaut sont définis dans la déclaration de la fonction.
  • Ils permettent de simplifier les appels en évitant de fournir des arguments redondants.
  • Tous les paramètres à droite d’un paramètre par défaut doivent aussi avoir une valeur par défaut.

3. Les fonctions inline

Une fonction inline est une fonction pour laquelle le compilateur remplace les appels par le code source directement, afin de réduire le surcoût des appels de fonction. Cela peut améliorer les performances pour des fonctions courtes.

Exemple en C++

#include <iostream>
using namespace std;

inline int carre(int x) {
    return x * x;
}

int main() {
    cout << "Le carré de 5 est : " << carre(5) << endl;  // Résultat : 25
    cout << "Le carré de 7 est : " << carre(7) << endl;  // Résultat : 49
    return 0;
}

Points clés :

  • Utilisez inline pour les fonctions courtes et critiques en termes de performances.
  • Attention : Trop de fonctions inline peuvent augmenter la taille du binaire (overhead) et réduire les performances globales.
  • Depuis C++17 : Les définitions de fonctions dans des fichiers d’en-tête (.h) sont implicitement considérées comme inline.

4. Les fonctions lambda

Les lambdas sont des fonctions anonymes définies directement dans le code. Elles sont utiles pour des opérations simples, comme des callbacks ou des expressions mathématiques.

Syntaxe de base

[capture](paramètres) -> type_retour {
    // Corps de la fonction
}

Concepts clés des lambdas

  • Captures : Détermine quelles variables du scope environnant sont accessibles.
    • [&] : Capture toutes les variables par référence.
    • [=] : Capture toutes les variables par copie.
    • [x] : Capture uniquement la variable x par copie.
    • [&x] : Capture uniquement la variable x par référence.
    • [=, &y] : Capture tout par copie sauf y, qui est capturé par référence.

(Re)Voir l'article sur la différence entre les pointeurs et les références.

  • Parameters : Liste des arguments passés à la fonction. La syntaxe est similaire à celle des fonctions classiques.

    • Exemple : [](int a, int b) définit une lambda avec deux paramètres entiers a et b.
  • Return type : Type de retour de la fonction.

    • Optionnel, souvent déduit automatiquement par le compilateur.
    • Peut être spécifié explicitement avec -> type.
    • Exemple : [](int x) -> double { return x / 2.0; }.

Exemple simple :

#include <iostream>
using namespace std;

int main() {
    auto carre = [](int x) -> int { return x * x; };  // Fonction lambda
    cout << "Le carré de 7 est " << carre(7) << endl;

    auto afficher = [](string message) {
        cout << message << endl;
    };
    afficher("Bonjour depuis une lambda !");
    return 0;
}

Exemple avec capture :

#include <iostream>
using namespace std;

int main() {
    int facteur = 10;
    auto multiplier = [facteur](int x) {
        return x * facteur;
    };

    cout << "5 x 10 = " << multiplier(5) << endl;  // Affiche 50
    return 0;
}

Points clés

  • Les lambdas simplifient l’écriture de fonctions temporaires ou inline.
  • Très utiles avec des algorithmes STL comme std::for_each ou std::sort. Pour en savoir plus sur les algorithmes STL, consultez notre article : Introduction à la STL en C++.
  • Les captures permettent d'accéder aux variables environnantes :
    • Par copie (=) : Les variables sont copiées dans la lambda.
    • Par référence (&) : Les variables sont utilisées directement sans être copiées.

5. Exercice pratique

Énoncé :

  1. Implémentez une fonction afficher surchargée pour afficher des valeurs de différents types (entier, double, chaîne).
  2. Créez une fonction avec des paramètres par défaut pour calculer l’aire d’un rectangle (longueur et largeur, avec la largeur par défaut à 1).
  3. Utilisez une fonction lambda pour trier un tableau d’entiers par ordre décroissant.

Solution en C++

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// Surcharge de la fonction afficher
void afficher(int valeur) {
    cout << "Entier : " << valeur << endl;
}

void afficher(double valeur) {
    cout << "Double : " << valeur << endl;
}

void afficher(const string &valeur) {
    cout << "Chaîne : " << valeur << endl;
}

// Fonction avec paramètres par défaut
double calculerAire(double longueur, double largeur = 1.0) {
    return longueur * largeur;
}

int main() {
    // Appels de la fonction afficher
    afficher(42);
    afficher(3.14);
    afficher("Bonjour");

    // Appel de la fonction pour calculer l'aire
    cout << "Aire avec longueur 5 et largeur 2 : " << calculerAire(5, 2) << endl;
    cout << "Aire avec longueur 5 et largeur par défaut : " << calculerAire(5) << endl;

    // Tri d'un tableau avec une lambda
    vector<int> nombres = {5, 3, 8, 1, 9};
    sort(nombres.begin(), nombres.end(), [](int a, int b) {
        return a > b;  // Ordre décroissant
    });

    cout << "Tableau trié par ordre décroissant : ";
    for (int n : nombres) {
        cout << n << " ";
    }
    cout << endl;

    return 0;
}

Points clés

  • La surcharge permet d’utiliser le même nom de fonction pour des types différents.
  • Les paramètres par défaut simplifient les appels de fonctions en cas de valeurs souvent répétées.
  • Les lambdas permettent de créer des fonctions inline pour des opérations simples comme le tri.

Conclusion

Les fonctionnalités avancées des fonctions en C++ offrent des outils puissants pour écrire un code plus expressif et optimisé. La surcharge, les paramètres par défaut, les fonctions inline et les lambdas permettent d'adapter vos fonctions à des besoins variés tout en rendant votre code plus clair et efficace.

Dans le prochain article, nous explorerons les vecteurs et tableaux en C++, des outils incontournables pour manipuler des collections de données.


À propos de pointerlab

pointerlab s’inspire des fonctions lambda en C++, symbole de code concis, flexible et performant. C’est cette philosophie, mêlant rigueur et modernité, que nous mettons au service des défis de nos clients.

Experts C++ et Qt, nous maîtrisons des technologies de pointe comme Qt, OpenGL, OpenCV et Unreal Engine 5 pour concevoir des solutions logicielles sur mesure et robustes. Présents dans des secteurs exigeants tels que la simulation 3D, le médical ou l’aéronautique, nous allions innovation et expertise pour relever les défis les plus complexes.

Rejoignez la communauté C++ France sur Discord !

Développez votre réseau et boostez votre carrière avec la communauté C++ France