Vous êtes sur la page 1sur 34

Fonctions

Laurent SARRY
laurent.sarry@uca.fr
Quizz Wooclap #9
Passage d’arguments
• Rappel : il existe trois types de passages d’arguments
• par valeur : l’argument est copié dans la valeur du paramètre
correspondant de la fonction
• par référence : une référence vers l’argument est passée au
paramètre correspondant de la fonction (pas de copie). Utilisé pour
des paramètres de sortie destinés à être modifiés, ou pour des
paramètres d’entrée si référence constante
• par adresse : l’adresse de la variable en argument est passée à la
fonction plutôt que sa valeur. Le paramètre de la fonction doit donc
être un pointeur
Passage d’arguments
• Bonnes pratiques :
• Réserver le passage par valeur aux arguments en entrée et aux types
simples
• Privilégier le passage par référence pour les arguments en entrée
(const) ou en sortie dont la copie est lourde (structure ou classe)
• Réserver le passage par adresse aux pointeurs et aux tableaux de type
C (convertis en pointeurs)
Passage d’arguments
• Remarque : le passage par adresse permet de modifier la
valeur, mais pas l’adresse pointée. Pour modifier cette
dernière, il faut passer le pointeur par référence
void ValeurASix(int* ptr) int main()
{ {
*ptr = 6; int five{ 5 };
} int* ptr{ &five };
ValeurASix(ptr); // valeur 5 -> 6
void AdresseANull(int*& ptr)
AdresseANull(ptr); // pointeur null
{ return 0;
ptr = nullptr; }
}
Question #1
Question #2
Valeur retournée
• Retour par valeur : une copie est retournée à la fonction
appelante. Convient pour le retour de variables déclarées dans
la fonction ou d’arguments passés par valeur

int doubleValeur(int x)
{
int valeur{ x * 2 };
return valeur; // une copie est retournée
} // valeur détruite à la fin de la fonction
Valeur retournée
• Retour par adresse : une adresse de variable est retournée à la
fonction appelante. Ne convient pas pour une variable locale
dont la portée est limitée, mais pour allocation dynamique
int* AllocationTableau(int taille)
{
return new int[taille];
}
int main()
{
int* tab{ AllocationTableau(5) };
// utilisation du tableau tab
delete[] tab;
return 0;
}
Valeur retournée
• Retour par référence : une référence de variable est retournée
à la fonction appelante. Ne convient pas pour une variable
locale dont la portée est limitée, mais pour un argument
#include <array>
// Retourne une référence sur l'élément index du tableau
int& getElement(std::array<int, 25>& tab, int index)
{
return tab[index]; // tableau non détruit car argument
}
int main()
{
std::array<int, 25> tableau;
getElement(tableau, 10) = 5; // valeur 5 dans l'élément 10
return 0;
}
Valeur retournée
• Retour de plusieurs valeurs : le C++ ne permet pas de
retourner directement plusieurs valeurs
• Utiliser des arguments en sortie (par adresse ou par référence)
• Retourner une structure qui contient plusieurs champs
• Retourner un std::tuple
std::tuple<int, double> retourneTuple() int a2;
{ double b2;
return { 5, 6.7 }; std::tie(a2, b2) = retourneTuple();
} int a3;
int main() double b3;
{ // C++17
std::tuple s{ retourneTuple() }; auto [a3, b3]{ retourneTuple() };
int a1{ std::get<0>(s) }; return 0;
double b1{ std::get<1>(s) }; }
Question #3
Question #4
Question #5
Question #6
Pointeur de fonction
• Comme un pointeur est une variable qui contient l’adresse
d’une autre variable, un pointeur de fonction est une variable
qui contient l’adresse d’une fonction
• Exemple :

// fcnPtr un pointeur vers une fonction


// qui retourne un entier et ne prend pas d'argument
int (*fcnPtr)();

// fcnPtr un pointeur const vers une fonction


// qui retourne un entier et ne prend pas d'argument
int (*const fcnPtr)();
Pointeur de fonction
• Un pointeur de fonction peut être initialisé à partir d’une
fonction du même type
int fcn1() int main()
{ {
return 5; int (*fcnPtr)(){ &fcn1 };
} // conversion implicite en pointeur, adresse inutile
int fcn2() fcnPtr = fcn2;
{ // déréférencement implicite lors de l’appel du pointeur
return 6; std::cout << (*fcnPtr)() << " " << fcnPtr() << '\n';
} fcnPtr = &fcn3; // NOK mauvais type
double fcn3() return 0;
{ }
return 7.8;
}
Pointeur de fonction
• Passage de fonction en argument d’une autre fonction : en
général appelée fonction de rappel (callback)
• Exemple : les tris prennent une fonction de comparaison en argument
bool croissant(int x, int y)
{
return x > y;
}
void tri(int *tab, int size, bool (*comparaison)(int, int)) {...}
void main()
{
int tableau[]{ 5, 6, 2 };
tri(tableau, 3, croissant);
return 0;
}
Pointeur de fonction
• Utilisation de std::function : pour définir et stocker des
pointeurs de fonctions
int fcn1() int main()
{ {
return 5; std::function<int()> fcnPtr{ &fcn1 };
} fcnPtr = fcn2;
int fcn2() std::cout << fcnPtr() << '\n';
{ return 0;
return 6; }
}
Question #7
Question #8
Question #9
Arguments de la ligne de commande
• Ce sont des arguments passés au programme, lorsqu’il est
exécuté, sous la forme de chaines de caractères suivant le
nom du programme
C:\Programme.exe fichier_entree.txt fichier_sortie.txt
#include <iostream> Il y a 3 arguments:
int main(int argc, char* argv[]) 0 C:\Programme.exe
{ 1 fichier_entree.txt
std::cout << "Il y a " << argc << " arguments:\n"; 2 fichier_sortie.txt
// boucle sur les arguments
for (int compteur{ 0 }; compteur < argc; ++compteur)
{
std::cout << compteur << ' ' << argv[compteur] << '\n';
}
return 0;
}
Question #10
Fonction lambda
• Une fonction lambda est une fonction anonyme qui peut être
définie dans une autre fonction au plus près de son appel. Elle
est utilisée pour encapsuler un algorithme afin de pouvoir le
transmettre à une autre fonction
• syntaxe
[ zone de capture ] ( paramètres ) -> type de retour
{
instructions;
}

• exemple
// test de la parité avec une fonction lambda
return std::all_of(tab.begin(), tab.end(), [](int i){ return ((i % 2) == 0); });
Fonction lambda
• Il existe trois méthodes pour nommer une fonction lambda
• Un pointeur de fonction s’il n’y a pas de clause de capture
double (*addition1)(double, double){
[](double a, double b) {
return (a + b); } };

• Une std::function même avec clause de capture


std::function addition2{ // avant C++17, std::function<double(double, double)>
[](double a, double b) {
return (a + b); } };

• La déduction de type automatique


auto addition3{
[](double a, double b) {
return (a + b); } };
Fonction lambda
• Fonction lambda générique : utilise auto pour déduire le type
des paramètres (C++14, existe également pour les fonctions
non lambda depuis C++20)

auto addition{
[](auto a, auto b) {
return (a + b);
}
};
Fonction lambda
• Déduction du type de retour : automatique mais doit être
spécifié en cas d’ambiguïté
#include <iostream>
int main()
{
auto division{ [](int x, int y, bool bInteger) -> double {
if (bInteger)
return x / y;
else
return static_cast<double>(x) / y;
} };
std::cout << division(3, 2, true) << '\n';
std::cout << division(3, 2, false) << '\n';
return 0;
}
Fonction lambda
• Zone de capture : par défaut, une fonction lambda est isolée
du reste du code et n’a pas accès aux variables de la fonction
où elle est définie
• La zone de capture permet de capturer des variables par valeur ou
par référence
• Par défaut, les variables sont capturées par valeur constante. Si on
veut les modifier dans la fonction lambda, il faut utiliser le mot clé
mutable
Fonction lambda
• Zone de capture : capture par valeur constante
int compteur{ };
auto incremente{
[compteur]() {
++compteur; // interdit valeur constante
}
};

incremente();
Fonction lambda
• Zone de capture : capture par valeur
int compteur{ };
auto incremente{
[compteur]() mutable {
++compteur; // modification de la copie locale
}
};

incremente();
Fonction lambda
• Zone de capture : capture par référence
int compteur{ };
auto incremente{
[&compteur]() {
++compteur; // modification de la variable compteur
}
};

incremente();
Fonction lambda
• Zone de capture par défaut : il est possible de capturer toutes
les variables par valeur avec = et par référence avec &
int varInt{ 5 };
double varDouble{ 10.0 };
std::vector<int> vectInt{};

// capture de varInt et varDouble par valeur et de vectInt par


référence
[varInt, varDouble, &vectInt](){};

// capture vectInt par référence et tout le reste par valeur


[=, &vectInt](){};

// capture varDouble par valeur et tout le reste par référence


[&, varDouble](){};
Question #11

Vous aimerez peut-être aussi