SlideShare une entreprise Scribd logo
Les fonctions lambdas 
en C++11 et C++14 
Montpellier C++, 21 oct. 2014 
http://www.meetup.com/Montpellier-CPP/ 
Aurélien Regat-Barrel 
http://cyberkarma.net
Ne pas confondre...
Lλmbdλ ? 
Les lambdas sont des fonctions anonymes... 
Wikipedia : « Les fonctions anonymes sont des 
fonctions n'ayant pas de nom. » 
En gros, c'est une fonction avec : 
● un corps 
● (éventuellement) des paramètres 
● (éventuellement) un type de retour 
mais pas de nom ! 
Mais alors, comment ça s'utilise ?
Principe 
Alors qu'une fonction nommée peut être référencée avant ou après 
sa définition, une expression lambda est référencée à l'endroit de 
sa création. 
Il n'y a pas donc pas de déclaration de symbole, seulement une 
définition de bloc fonction. 
● Généralement à usage unique, temporaire. 
● Typiquement destinée à être passée en argument à une autre 
fonction… 
Lambda = callback sous stéroïde ?
Syntaxe générale 
[] // lambda introducer : capture de variables 
() // paramètre[s] de la fonction (facultatif) 
{ 
// corps de la fonction 
} (); // appel de la fonction (facultatif) 
// Exemple : 
auto f = [](int i) { return i + 10; }; 
f(1); 
std::vector<int> v = { 1, 2, 3, 4 }; 
std::transform(cbegin(v), cend(v), begin(v), f); 
std::for_each(cbegin(v), cend(v), [](int n) { 
std::cout << n << ' '; 
}); 
// affiche : 11 12 13 14
Closure et foncteur 
Un objet fonction créé via une lambda est une fermeture lexicale 
(closure) : il y a capture de paramètres. 
std::vector<int> v = { 0, 5, 10, 15, 20, 25 }; 
auto it = std::find_if(v.cbegin(), v.cend(), 
[](int i) { return i > 0 && i < 10; } 
); 
Le compilateur génère quelque chose qui ressemble à : 
struct Lambda1 { 
bool operator()(int i) const { return i > 0 && i < 10; } 
}; 
auto it = std::find_if(v.cbegin(), v.cend(), Lambda1()); 
Lambda = sucre syntactique de foncteur ?
<algorithm> 
Les lambdas se combinent parfaitement avec les algorithmes de la 
STL : 
● all_of 
● any_of 
● count_if 
● equal 
● mismatch 
● none_of 
● copy_if 
● generate 
● remove_if 
● sort 
● transform 
● ... 
● binary_search 
● find_if 
● find_if_not 
● for_each 
● includes 
● minmax 
Il est désormais plus facile d'utiliser ces algorithmes au lieu de les 
recoder / dissimuler via une boucle for. 
● boucle for = goto moderne ?
std::async 
Les lambdas sont aussi très pratiques en programmation concurrente / 
asynchrone : 
#include <future> 
// exécution asynchrone d'une tâche 
std::future<int> f = std::async([] { 
// calcul qui prend du temps… 
return result; 
}); 
// faire autre chose... 
// résultat de l'opération asynchrone 
int r = f.get();
Qt 5 
Depuis Qt5, elles peuvent être utilisées comme slot : 
QTcpSocket * socket = new QTcpSocket; 
socket->connectToHost("www.example.com", 80); 
QObject::connect(socket, &QTcpSocket::connected, [socket]() { 
socket->write(QByteArray("GET index.htmlrn")); 
}); 
QObject::connect(socket, &QTcpSocket::readyRead, [socket]() { 
qDebug() << "GOT DATA " << socket->readAll(); 
}); 
QObject::connect(socket, &QTcpSocket::disconnected, [socket]() { 
qDebug() << "DISCONNECTED"; 
socket->deleteLater(); 
});
Possibilités 
A peu près tout ce qui est autorisé dans une fonction nommée l'est aussi 
dans une lambda : 
● Expressions complexes 
● Multiples return 
● Lancer / attrapper des exceptions 
● Définir d'autres lambdas 
● … 
Mais l'idée générale est d'avoir quelque chose de concis, en lien étroit 
avec le contexte de son utilisation. 
Ce qui n'est pas possible : accéder au pointeur this du foncteur généré 
par le compilateur. 
● Une lambda ne peut donc pas s'appeler elle-même de façon directe.
Type d'une lambda 
Une lambda qui ne capture aucune variable peut être convertie en 
pointeur de fonction. Exemple : 
std::atexit([]{ 
LOG_INFO("Exiting..."); 
}); 
Mais le type de la lambda elle-même est non spécifié. Chaque lambda 
introduit en effet un nouveau type qui lui est spécifique : 
int main() { 
[] { 
std::cout << __FUNCTION__ << "n"; 
}(); 
} 
main::<lambda_a8379e393dcd443e8683ae1a31573b62>::operator ()
std::function 
On ne peut donc pas spécifier de type « lambda » en paramètre / retour 
d'une fonction (puisqu'il n'y a pas de tel type global). 
Pour ce faire, on utilisera std::function qui peut encapsuler une 
lambda, mais aussi d'autre objets appelables : 
● Un foncteur 
● Un pointeur de fonction « à la C » 
● Un objet fonction créé avec std::bind 
#include <functional> 
void call(std::function<void(void)> f) { 
f(); 
} call([] { std::exit(1); }); 
call(std::bind(&std::exit, 1));
Durée de vie 
Les fermetures lexicales peuvent « survivre » aux fonctions qui les ont 
créées : 
int a = 1; 
std::function<int(int)> returnClosure() { 
return [](int x) { 
return (x + a); 
}; 
} 
int main() { 
auto f = returnClosure(); 
std::cout << f(1); // affiche 2 
a += 1; 
std::cout << f(1); // affiche 3 
}
Types de retour des lambdas 
Préciser le type de retour est optionnel quand : 
● il s'agit de void 
● le corps de la fonction lambda consiste en un return expr; 
Autrement le type de retour doit être spécifié via la syntaxe « à la traîne » 
(trailing return type) : 
auto f = [](int i) -> int { 
g(); 
return i + h(); 
}; 
C++14 assouplit les règles à ce niveau.
Trailing return type notation 
Cette syntaxe à la traîne : 
● Est la seule façon de préciser le type de retour des lambdas quand 
cela est nécessaire 
● est permise pour n'importe quelle fonction (précédée de auto), y 
compris main() 
● se combine souvent avec decltype 
void f(int x); // syntaxe traditionnelle 
auto f(int x)->void; // déclaration équivalente 
class A { 
public: 
bool f1() const; 
auto f2() const -> bool; 
};
Capture de variables 
Pour référencer des variables locales (non statiques), la lambda doit les 
capturer (principe de la closure) : 
std::vector<int> v = { 5, 10, 20 }; 
int minVal = 10; 
// capture de minVal 
auto l = [minVal](int i) { 
return i > minVal; 
}; 
// affiche 20 
std::cout << *std::find_if( 
class Lambda { 
public: 
Lambda(int m) : minVal(m) {} 
bool operator()(int i) const { 
return i > minVal; 
} 
private: 
int minVal; 
}; 
v.cbegin(), v.cend(), l); 
C++11 : le type capturé doit être copiable (donc pas de unique_ptr) 
C++14 introduit la généralisation de capture
Capture de variables 
La capture peut aussi être effectuée par référence : 
auto l = [&minVal](int i) { 
return i > minVal; 
}; 
class Lambda { 
public: 
Lambda(int m) : minVal(m) {} 
bool operator()(int i) const { 
return i > minVal; 
} 
private: 
int & minVal; 
};
Généralités 
On peut combiner les types de capture : 
int minVal = 10; 
int maxVal = 20; 
auto l = [&minVal, maxVal](int i) { 
return i > minVal && i < maxVal; 
}; 
class Lambda { 
public: 
Lambda(int m1, int m2) : minVal(m1), maxVal(m2) {} 
bool operator()(int i) const { 
return i > minVal && i < maxVal; 
} 
private: 
int & minVal; 
int maxVal; 
};
Généralités 
Le mode de capture par défaut peut aussi être spécifié : 
int minVal = 10; 
int maxVal = 20; 
auto f1 = [=](int i) { // défaut : par valeur 
return i > minVal && i < maxVal; 
}; 
auto f2 = [&](int i) { // défaut : par référence 
return i > minVal && i < maxVal; 
}; 
Quand un mode de capture par défaut est spécifié, les variables 
capturées n'ont plus besoin d'être listées.
Généralités 
On peut bien sûr ajuster le mode de capture au besoin : 
int minVal = 10; 
int maxVal = 20; 
auto f = [=, &minVal](int i) { 
return i > minVal && i < maxVal; 
}; 
minVal est capturé par référence, maxVal par valeur.
Capturer des membres de classe 
On ne peut pas capturer directement les membres d'une classe : 
class A { 
public: 
void f() { 
// erreur: this->minVal ne peut pas être capturé ! 
auto l = [minVal](int i) { 
return i > minVal; 
}; 
} 
private: 
std::vector<int> data; 
int minVal; 
};
Généralités 
Pour accéder aux membres d'une classe, il faut capturer this : 
class A { 
public: 
void f() { 
/// OK: "minVal" => "this->minVal" 
auto l = [this](int i) { 
return i > minVal; 
}; 
} 
private: 
std::vector<int> data; 
int minVal; 
}; 
Tous les membres de la classe (même privés) sont accessibles car le 
type de la closure fait partie intégrante de la classe où il a été défini.
Capture implicite de this 
On peut aussi préciser un mode de capture par défaut afin de capturer 
implicitement this : 
class A { 
void f() { 
auto it = std::find_if(data.cbegin(), data.cend(), 
// OK: copie this dans la closure 
[=](int i) { return i > minVal; } 
); 
} 
int minVal = 0; 
std::vector<int> data; 
};
Capture de this par référence 
Version avec capture implicite par référence : 
void A::f() { 
auto it = std::find_if(data.cbegin(), data.cend(), 
// OK: maintient une référence vers this dans la closure 
[&](int i) { return i > minVal; } 
); 
} 
A noter que : 
● la capture de this par référence est potentiellement plus lente à 
cause de la double indirection (reference->this->minVal). 
● comme toute référence, l'objet référencé peut ne plus exister… 
● de même que la capture de this !
Capture de this par référence 
Si un objet est capturé par référence, celui-ci peut être modifié : 
int n = 10; 
auto f = [&n] { 
n = 20; // OK 
}; 
struct Lambda1 { 
Lambda1(int & N) : n(N) {} 
void operator()() const { 
n = 20; // OK (bien que fonction const!) 
} 
int & n; 
}; 
Et oui : c'est l'objet référencé qui est modifié, pas la référence !
Capture de this par référence 
Par contre, cela ne fonctionne pas avec une capture par copie : 
int n = 10; 
auto f = [n] { 
n = 20; // « impossible de modifier une capture par valeur 
dans une expression lambda non mutable » 
}; 
struct Lambda1 { 
Lambda1(int N) : n(N) {} 
void operator()() const { 
n = 20; // Erreur : modification depuis const ! 
} 
int n; 
}; 
Une lambda devrait en effet produire le même résultat si appelée deux 
fois de suite avec les mêmes arguments (stateless).
Lambda mutable 
Pour pouvoir modifier une variable capturée par copie, il faut que la 
lambda soit mutable : 
int n = 10; 
auto f = [n]() mutable { 
n = 20; // OK 
}; 
struct Lambda1 { 
Lambda1(int N) : n(N) {} 
void operator()() { 
n = 20; 
} 
int n; 
}; 
operator() n'est plus const.
Lambdas en C++14 
C++14 vient compléter C++11 à divers niveaux. 
En ce qui concerne les lambdas, la modification majeure est la possibilité 
d'utiliser auto comme type des paramètres. 
Les lambdas deviennent alors génériques (polymorphiques).
Lambda générique 
auto add = [](auto a, auto b) { return a + b; } 
struct Lambda { 
template<typename T1, typename T2> 
auto operator()(T1 a, T2 b) const -> decltype(a + b) { 
return a + b; 
Lambda = foncteur sous stéroïde ? 
} 
};
Risques / abus d'utilisation ? 
Prepare for unforeseen 
consequences...
Lambda vs fonction nommée 
auto isValidId = [](QString s) { 
return s.size() >= 4 && 
s.size() <= 8) && 
(s.toUpper() == s); 
}; 
for (auto & item : group1){ 
if (isValidId(item->id)) 
// ... 
} 
for (auto & item : group2) { 
if (isValidId(item->id)) 
// ... 
} 
static bool isValidId(QString s) { 
return s.size() >= 4 && 
s.size() <= 8) && 
(s.toUpper() == s); 
}; 
for (auto & item : group1){ 
if (isValidId(item->id)) 
// ... 
} 
for (auto & item : group2) { 
if (isValidId(item->id)) 
// ... 
} 
Si une lambda doit être utilisée plusieurs fois, faut-il lui préférer une 
fonction nommée (locale) ?
Code déstabilisant à lire 
class ScopeGuard { 
public: 
ScopeGuard(std::function<void(void)> F) : f(F) {} 
~ScopeGuard() { f(); } 
std::function<void(void)> f; 
}; 
int main() { 
FILE * file = nullptr; 
ScopeGuard guard([&file] { 
if (file != nullptr) { 
fclose(file); 
} 
}); 
// ... 
file = fopen("test.txt", "r"); 
}
Attention à la capture par référence 
class A { 
public: 
int compute(); // résultat long à calculer 
}; 
future<int> computeAsync(shared_ptr<A> pA) { 
return async([&pA]() { 
return pA->compute(); 
}); 
} 
int main() { 
auto f = computeAsync(make_shared<A>()); 
// ... 
cout << f.get(); 
} 
Le pointeur intelligent reçu a 
été capturé sous forme de 
référence… son compteur 
d'utilisation n'est pas 
incrémenté ! 
Ce pointeur intelligent est 
un temporaire qui est 
détruit une fois la fonction 
computeAsync() appelée.
Récapitulatif 
Les expressions lambda génèrent des fermetures lexicales (closures). 
Le contexte d'appel peut être capturé par valeur ou par référence. 
Le type de retour - si spécifié - utilise la syntaxe dite « à la traîne ». 
Les fermetures peuvent être conservées avec auto ou std::function. 
● Attention à la durée de vie des variables capturées ! 
Les lambdas devraient rester concises et spécifiques à un contexte 
particulier (utilisées à un seul endroit). 
C++14 ajoute le support de paramètres auto, de la capture généralisée, 
ainsi que plus de souplesse au niveau de la déduction du type de retour.
Conclusion 
Au final, une lambda c'est quoi ? 
Du sucre syntactique de foncteur sous stéroïde !

Contenu connexe

Tendances

Python
PythonPython
récursivité algorithmique et complexité algorithmique et Les algorithmes de tri
récursivité algorithmique et complexité algorithmique et Les algorithmes de trirécursivité algorithmique et complexité algorithmique et Les algorithmes de tri
récursivité algorithmique et complexité algorithmique et Les algorithmes de tri
Yassine Anddam
 
Chapitre1: Langage Python
Chapitre1: Langage PythonChapitre1: Langage Python
Chapitre1: Langage Python
Aziz Darouichi
 
Cours structures des données (langage c)
Cours structures des données (langage c)Cours structures des données (langage c)
Cours structures des données (langage c)
rezgui mohamed
 
Python avancé : Classe et objet
Python avancé : Classe et objetPython avancé : Classe et objet
Python avancé : Classe et objet
ECAM Brussels Engineering School
 
COURS_PYTHON_22.ppt
COURS_PYTHON_22.pptCOURS_PYTHON_22.ppt
COURS_PYTHON_22.ppt
IbtissameAbbad1
 
Java cours n° 2 - classe-objet-constructeur
Java   cours n° 2 - classe-objet-constructeurJava   cours n° 2 - classe-objet-constructeur
Java cours n° 2 - classe-objet-constructeur
Abdelwahab Naji
 
Cours langage c
Cours langage cCours langage c
Cours langage c
coursuniv
 
Présentation python
Présentation pythonPrésentation python
Présentation python
Sarah
 
Chap3 programmation modulaire en python
Chap3 programmation modulaire en pythonChap3 programmation modulaire en python
Chap3 programmation modulaire en python
Mariem ZAOUALI
 
Chapitre4: Pointeurs et références
Chapitre4: Pointeurs et références Chapitre4: Pointeurs et références
Chapitre4: Pointeurs et références
Aziz Darouichi
 
Cours python
Cours pythonCours python
Cours pythonsalmazen
 
Chapitre2fonctionscppv2019
Chapitre2fonctionscppv2019Chapitre2fonctionscppv2019
Chapitre2fonctionscppv2019
Aziz Darouichi
 
Python.pptx
Python.pptxPython.pptx
Python.pptx
Jaouad Rachek
 
Cours système d'exploitation
Cours système d'exploitationCours système d'exploitation
Cours système d'exploitationAmel Morchdi
 
Chap1: Cours en C++
Chap1: Cours en C++Chap1: Cours en C++
Chap1: Cours en C++
Aziz Darouichi
 
Support de cours technologie et application m.youssfi
Support de cours technologie et application m.youssfiSupport de cours technologie et application m.youssfi
Support de cours technologie et application m.youssfi
ENSET, Université Hassan II Casablanca
 
Exposé langage-b
Exposé langage-bExposé langage-b
Exposé langage-b
Donia Hammami
 
Formation python
Formation pythonFormation python
Formation python
j_lipaz
 
Formation python 3
Formation python 3Formation python 3
Formation python 3
WajihBaghdadi1
 

Tendances (20)

Python
PythonPython
Python
 
récursivité algorithmique et complexité algorithmique et Les algorithmes de tri
récursivité algorithmique et complexité algorithmique et Les algorithmes de trirécursivité algorithmique et complexité algorithmique et Les algorithmes de tri
récursivité algorithmique et complexité algorithmique et Les algorithmes de tri
 
Chapitre1: Langage Python
Chapitre1: Langage PythonChapitre1: Langage Python
Chapitre1: Langage Python
 
Cours structures des données (langage c)
Cours structures des données (langage c)Cours structures des données (langage c)
Cours structures des données (langage c)
 
Python avancé : Classe et objet
Python avancé : Classe et objetPython avancé : Classe et objet
Python avancé : Classe et objet
 
COURS_PYTHON_22.ppt
COURS_PYTHON_22.pptCOURS_PYTHON_22.ppt
COURS_PYTHON_22.ppt
 
Java cours n° 2 - classe-objet-constructeur
Java   cours n° 2 - classe-objet-constructeurJava   cours n° 2 - classe-objet-constructeur
Java cours n° 2 - classe-objet-constructeur
 
Cours langage c
Cours langage cCours langage c
Cours langage c
 
Présentation python
Présentation pythonPrésentation python
Présentation python
 
Chap3 programmation modulaire en python
Chap3 programmation modulaire en pythonChap3 programmation modulaire en python
Chap3 programmation modulaire en python
 
Chapitre4: Pointeurs et références
Chapitre4: Pointeurs et références Chapitre4: Pointeurs et références
Chapitre4: Pointeurs et références
 
Cours python
Cours pythonCours python
Cours python
 
Chapitre2fonctionscppv2019
Chapitre2fonctionscppv2019Chapitre2fonctionscppv2019
Chapitre2fonctionscppv2019
 
Python.pptx
Python.pptxPython.pptx
Python.pptx
 
Cours système d'exploitation
Cours système d'exploitationCours système d'exploitation
Cours système d'exploitation
 
Chap1: Cours en C++
Chap1: Cours en C++Chap1: Cours en C++
Chap1: Cours en C++
 
Support de cours technologie et application m.youssfi
Support de cours technologie et application m.youssfiSupport de cours technologie et application m.youssfi
Support de cours technologie et application m.youssfi
 
Exposé langage-b
Exposé langage-bExposé langage-b
Exposé langage-b
 
Formation python
Formation pythonFormation python
Formation python
 
Formation python 3
Formation python 3Formation python 3
Formation python 3
 

En vedette

Introduction au lock-free programming avec std::atomics
Introduction au lock-free programming avec std::atomicsIntroduction au lock-free programming avec std::atomics
Introduction au lock-free programming avec std::atomics
Aurélien Regat-Barrel
 
Les nouveautés de C++11 : Ecrire du C++ Moderne
Les nouveautés de C++11 : Ecrire du C++ ModerneLes nouveautés de C++11 : Ecrire du C++ Moderne
Les nouveautés de C++11 : Ecrire du C++ Moderne
Microsoft
 
What's New in C++ 11?
What's New in C++ 11?What's New in C++ 11?
What's New in C++ 11?
Sasha Goldshtein
 
C++11 & C++14
C++11 & C++14C++11 & C++14
C++11 & C++14
CyberPlusIndia
 
C# 5 versus Java 8... Quand C++ 11 s'invite à la fête
C# 5 versus Java 8... Quand C++ 11 s'invite à la fêteC# 5 versus Java 8... Quand C++ 11 s'invite à la fête
C# 5 versus Java 8... Quand C++ 11 s'invite à la fête
Fabrice JEAN-FRANCOIS
 
C++11
C++11C++11
C++ Generators and Property-based Testing
C++ Generators and Property-based TestingC++ Generators and Property-based Testing
C++ Generators and Property-based Testing
Sumant Tambe
 
Fun with Lambdas: C++14 Style (part 2)
Fun with Lambdas: C++14 Style (part 2)Fun with Lambdas: C++14 Style (part 2)
Fun with Lambdas: C++14 Style (part 2)
Sumant Tambe
 
Campagne internationales
Campagne internationalesCampagne internationales
Campagne internationales
Liisa Dumas
 
Penséesde confucius
Penséesde confuciusPenséesde confucius
Penséesde confuciuslyago
 
Retrospective 2016
Retrospective 2016Retrospective 2016
Retrospective 2016
williamslouit
 
Lumieres
LumieresLumieres
Lumieres
datthieu
 
04 presentation-composition d-instructions
04 presentation-composition d-instructions04 presentation-composition d-instructions
04 presentation-composition d-instructions
fschoubben
 
Entreprendre à Châlons - Aides fiscales et sociales
Entreprendre à Châlons - Aides fiscales et socialesEntreprendre à Châlons - Aides fiscales et sociales
Entreprendre à Châlons - Aides fiscales et sociales
ac2s_expertise
 
Donde Cruzan Los Brujos (Taisha Abelar)
Donde Cruzan Los Brujos (Taisha Abelar)Donde Cruzan Los Brujos (Taisha Abelar)
Donde Cruzan Los Brujos (Taisha Abelar)
Euler
 
Propuesta de una interfaz interoperable para una Red Nacional Distribuida de ...
Propuesta de una interfaz interoperable para una Red Nacional Distribuida de ...Propuesta de una interfaz interoperable para una Red Nacional Distribuida de ...
Propuesta de una interfaz interoperable para una Red Nacional Distribuida de ...
Manuel Mujica
 
Violin part 1
Violin part 1Violin part 1
Violin part 1
Saulo Gomes
 
Intervention à l'INSEEC pour le Digital Innovation Challenge #2014
Intervention à l'INSEEC pour le Digital Innovation Challenge #2014Intervention à l'INSEEC pour le Digital Innovation Challenge #2014
Intervention à l'INSEEC pour le Digital Innovation Challenge #2014
Cédric MONTET
 
Inteligencia emocional y su relación con la felicidad
Inteligencia emocional y su relación con la felicidadInteligencia emocional y su relación con la felicidad
Inteligencia emocional y su relación con la felicidad
Euler
 

En vedette (20)

Introduction au lock-free programming avec std::atomics
Introduction au lock-free programming avec std::atomicsIntroduction au lock-free programming avec std::atomics
Introduction au lock-free programming avec std::atomics
 
Les nouveautés de C++11 : Ecrire du C++ Moderne
Les nouveautés de C++11 : Ecrire du C++ ModerneLes nouveautés de C++11 : Ecrire du C++ Moderne
Les nouveautés de C++11 : Ecrire du C++ Moderne
 
What's New in C++ 11?
What's New in C++ 11?What's New in C++ 11?
What's New in C++ 11?
 
C++11 & C++14
C++11 & C++14C++11 & C++14
C++11 & C++14
 
C# 5 versus Java 8... Quand C++ 11 s'invite à la fête
C# 5 versus Java 8... Quand C++ 11 s'invite à la fêteC# 5 versus Java 8... Quand C++ 11 s'invite à la fête
C# 5 versus Java 8... Quand C++ 11 s'invite à la fête
 
C++11
C++11C++11
C++11
 
C++ Generators and Property-based Testing
C++ Generators and Property-based TestingC++ Generators and Property-based Testing
C++ Generators and Property-based Testing
 
C++14 Overview
C++14 OverviewC++14 Overview
C++14 Overview
 
Fun with Lambdas: C++14 Style (part 2)
Fun with Lambdas: C++14 Style (part 2)Fun with Lambdas: C++14 Style (part 2)
Fun with Lambdas: C++14 Style (part 2)
 
Campagne internationales
Campagne internationalesCampagne internationales
Campagne internationales
 
Penséesde confucius
Penséesde confuciusPenséesde confucius
Penséesde confucius
 
Retrospective 2016
Retrospective 2016Retrospective 2016
Retrospective 2016
 
Lumieres
LumieresLumieres
Lumieres
 
04 presentation-composition d-instructions
04 presentation-composition d-instructions04 presentation-composition d-instructions
04 presentation-composition d-instructions
 
Entreprendre à Châlons - Aides fiscales et sociales
Entreprendre à Châlons - Aides fiscales et socialesEntreprendre à Châlons - Aides fiscales et sociales
Entreprendre à Châlons - Aides fiscales et sociales
 
Donde Cruzan Los Brujos (Taisha Abelar)
Donde Cruzan Los Brujos (Taisha Abelar)Donde Cruzan Los Brujos (Taisha Abelar)
Donde Cruzan Los Brujos (Taisha Abelar)
 
Propuesta de una interfaz interoperable para una Red Nacional Distribuida de ...
Propuesta de una interfaz interoperable para una Red Nacional Distribuida de ...Propuesta de una interfaz interoperable para una Red Nacional Distribuida de ...
Propuesta de una interfaz interoperable para una Red Nacional Distribuida de ...
 
Violin part 1
Violin part 1Violin part 1
Violin part 1
 
Intervention à l'INSEEC pour le Digital Innovation Challenge #2014
Intervention à l'INSEEC pour le Digital Innovation Challenge #2014Intervention à l'INSEEC pour le Digital Innovation Challenge #2014
Intervention à l'INSEEC pour le Digital Innovation Challenge #2014
 
Inteligencia emocional y su relación con la felicidad
Inteligencia emocional y su relación con la felicidadInteligencia emocional y su relación con la felicidad
Inteligencia emocional y su relación con la felicidad
 

Similaire à Les fonctions lambdas en C++11 et C++14

Chapitre6: Surcharge des opérateurs
Chapitre6:  Surcharge des opérateursChapitre6:  Surcharge des opérateurs
Chapitre6: Surcharge des opérateurs
Aziz Darouichi
 
Chapitre 04 : les fonctions
Chapitre 04 : les fonctionsChapitre 04 : les fonctions
Chapitre 04 : les fonctions
L’Université Hassan 1er Settat
 
Chap2fonctionscpp
Chap2fonctionscppChap2fonctionscpp
Chap2fonctionscpp
Aziz Darouichi
 
Cours de C++ / Tronc commun deuxième année ISIMA
Cours de C++ / Tronc commun deuxième année ISIMACours de C++ / Tronc commun deuxième année ISIMA
Cours de C++ / Tronc commun deuxième année ISIMA
Loic Yon
 
Interception de signal avec dump de la pile d'appel
Interception de signal avec dump de la pile d'appelInterception de signal avec dump de la pile d'appel
Interception de signal avec dump de la pile d'appel
Thierry Gayet
 
02 Spécificité du C++ COURS SYS SYSSSSSS
02 Spécificité du C++  COURS SYS SYSSSSSS02 Spécificité du C++  COURS SYS SYSSSSSS
02 Spécificité du C++ COURS SYS SYSSSSSS
AyoubElmrabet6
 
C++11 en 12 exemples simples
C++11 en 12 exemples simplesC++11 en 12 exemples simples
C++11 en 12 exemples simples
Pethrvs
 
C++11
C++11C++11
C++11
Pethrvs
 
De Java à .NET
De Java à .NETDe Java à .NET
De Java à .NET
Michel Salib
 
Scala : programmation fonctionnelle
Scala : programmation fonctionnelleScala : programmation fonctionnelle
Scala : programmation fonctionnelle
MICHRAFY MUSTAFA
 
Les bases de la programmation en JAVA
Les bases de la programmation  en JAVA   Les bases de la programmation  en JAVA
Les bases de la programmation en JAVA
Asmaa BENGUEDDACH
 
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soatSOAT
 
Cours de C++, en français, 2002 - Cours 1.5
Cours de C++, en français, 2002 - Cours 1.5Cours de C++, en français, 2002 - Cours 1.5
Cours de C++, en français, 2002 - Cours 1.5
Laurent BUNIET
 
Java 8 - lambda
Java 8 - lambdaJava 8 - lambda
Java 8 - lambda
Franck SIMON
 
Memo java
Memo javaMemo java
Memo java
Ghazouani Mahdi
 
Chap1_Entrees_Sorties.pptx
Chap1_Entrees_Sorties.pptxChap1_Entrees_Sorties.pptx
Chap1_Entrees_Sorties.pptx
BelhassenGuettat2
 
Memojava 100604104941-phpapp02
Memojava 100604104941-phpapp02Memojava 100604104941-phpapp02
Memojava 100604104941-phpapp02
Rahma Boufalgha
 

Similaire à Les fonctions lambdas en C++11 et C++14 (20)

Chapitre6: Surcharge des opérateurs
Chapitre6:  Surcharge des opérateursChapitre6:  Surcharge des opérateurs
Chapitre6: Surcharge des opérateurs
 
Chapitre 04 : les fonctions
Chapitre 04 : les fonctionsChapitre 04 : les fonctions
Chapitre 04 : les fonctions
 
Chap2fonctionscpp
Chap2fonctionscppChap2fonctionscpp
Chap2fonctionscpp
 
Cours de C++ / Tronc commun deuxième année ISIMA
Cours de C++ / Tronc commun deuxième année ISIMACours de C++ / Tronc commun deuxième année ISIMA
Cours de C++ / Tronc commun deuxième année ISIMA
 
Interception de signal avec dump de la pile d'appel
Interception de signal avec dump de la pile d'appelInterception de signal avec dump de la pile d'appel
Interception de signal avec dump de la pile d'appel
 
C4 fonctions
C4 fonctionsC4 fonctions
C4 fonctions
 
02 Spécificité du C++ COURS SYS SYSSSSSS
02 Spécificité du C++  COURS SYS SYSSSSSS02 Spécificité du C++  COURS SYS SYSSSSSS
02 Spécificité du C++ COURS SYS SYSSSSSS
 
C++11 en 12 exemples simples
C++11 en 12 exemples simplesC++11 en 12 exemples simples
C++11 en 12 exemples simples
 
C++11
C++11C++11
C++11
 
Ch08
Ch08Ch08
Ch08
 
De Java à .NET
De Java à .NETDe Java à .NET
De Java à .NET
 
Scala : programmation fonctionnelle
Scala : programmation fonctionnelleScala : programmation fonctionnelle
Scala : programmation fonctionnelle
 
Les bases de la programmation en JAVA
Les bases de la programmation  en JAVA   Les bases de la programmation  en JAVA
Les bases de la programmation en JAVA
 
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
 
Cours de C++, en français, 2002 - Cours 1.5
Cours de C++, en français, 2002 - Cours 1.5Cours de C++, en français, 2002 - Cours 1.5
Cours de C++, en français, 2002 - Cours 1.5
 
Ch11
Ch11Ch11
Ch11
 
Java 8 - lambda
Java 8 - lambdaJava 8 - lambda
Java 8 - lambda
 
Memo java
Memo javaMemo java
Memo java
 
Chap1_Entrees_Sorties.pptx
Chap1_Entrees_Sorties.pptxChap1_Entrees_Sorties.pptx
Chap1_Entrees_Sorties.pptx
 
Memojava 100604104941-phpapp02
Memojava 100604104941-phpapp02Memojava 100604104941-phpapp02
Memojava 100604104941-phpapp02
 

Les fonctions lambdas en C++11 et C++14

  • 1. Les fonctions lambdas en C++11 et C++14 Montpellier C++, 21 oct. 2014 http://www.meetup.com/Montpellier-CPP/ Aurélien Regat-Barrel http://cyberkarma.net
  • 3. Lλmbdλ ? Les lambdas sont des fonctions anonymes... Wikipedia : « Les fonctions anonymes sont des fonctions n'ayant pas de nom. » En gros, c'est une fonction avec : ● un corps ● (éventuellement) des paramètres ● (éventuellement) un type de retour mais pas de nom ! Mais alors, comment ça s'utilise ?
  • 4. Principe Alors qu'une fonction nommée peut être référencée avant ou après sa définition, une expression lambda est référencée à l'endroit de sa création. Il n'y a pas donc pas de déclaration de symbole, seulement une définition de bloc fonction. ● Généralement à usage unique, temporaire. ● Typiquement destinée à être passée en argument à une autre fonction… Lambda = callback sous stéroïde ?
  • 5. Syntaxe générale [] // lambda introducer : capture de variables () // paramètre[s] de la fonction (facultatif) { // corps de la fonction } (); // appel de la fonction (facultatif) // Exemple : auto f = [](int i) { return i + 10; }; f(1); std::vector<int> v = { 1, 2, 3, 4 }; std::transform(cbegin(v), cend(v), begin(v), f); std::for_each(cbegin(v), cend(v), [](int n) { std::cout << n << ' '; }); // affiche : 11 12 13 14
  • 6. Closure et foncteur Un objet fonction créé via une lambda est une fermeture lexicale (closure) : il y a capture de paramètres. std::vector<int> v = { 0, 5, 10, 15, 20, 25 }; auto it = std::find_if(v.cbegin(), v.cend(), [](int i) { return i > 0 && i < 10; } ); Le compilateur génère quelque chose qui ressemble à : struct Lambda1 { bool operator()(int i) const { return i > 0 && i < 10; } }; auto it = std::find_if(v.cbegin(), v.cend(), Lambda1()); Lambda = sucre syntactique de foncteur ?
  • 7. <algorithm> Les lambdas se combinent parfaitement avec les algorithmes de la STL : ● all_of ● any_of ● count_if ● equal ● mismatch ● none_of ● copy_if ● generate ● remove_if ● sort ● transform ● ... ● binary_search ● find_if ● find_if_not ● for_each ● includes ● minmax Il est désormais plus facile d'utiliser ces algorithmes au lieu de les recoder / dissimuler via une boucle for. ● boucle for = goto moderne ?
  • 8. std::async Les lambdas sont aussi très pratiques en programmation concurrente / asynchrone : #include <future> // exécution asynchrone d'une tâche std::future<int> f = std::async([] { // calcul qui prend du temps… return result; }); // faire autre chose... // résultat de l'opération asynchrone int r = f.get();
  • 9. Qt 5 Depuis Qt5, elles peuvent être utilisées comme slot : QTcpSocket * socket = new QTcpSocket; socket->connectToHost("www.example.com", 80); QObject::connect(socket, &QTcpSocket::connected, [socket]() { socket->write(QByteArray("GET index.htmlrn")); }); QObject::connect(socket, &QTcpSocket::readyRead, [socket]() { qDebug() << "GOT DATA " << socket->readAll(); }); QObject::connect(socket, &QTcpSocket::disconnected, [socket]() { qDebug() << "DISCONNECTED"; socket->deleteLater(); });
  • 10. Possibilités A peu près tout ce qui est autorisé dans une fonction nommée l'est aussi dans une lambda : ● Expressions complexes ● Multiples return ● Lancer / attrapper des exceptions ● Définir d'autres lambdas ● … Mais l'idée générale est d'avoir quelque chose de concis, en lien étroit avec le contexte de son utilisation. Ce qui n'est pas possible : accéder au pointeur this du foncteur généré par le compilateur. ● Une lambda ne peut donc pas s'appeler elle-même de façon directe.
  • 11. Type d'une lambda Une lambda qui ne capture aucune variable peut être convertie en pointeur de fonction. Exemple : std::atexit([]{ LOG_INFO("Exiting..."); }); Mais le type de la lambda elle-même est non spécifié. Chaque lambda introduit en effet un nouveau type qui lui est spécifique : int main() { [] { std::cout << __FUNCTION__ << "n"; }(); } main::<lambda_a8379e393dcd443e8683ae1a31573b62>::operator ()
  • 12. std::function On ne peut donc pas spécifier de type « lambda » en paramètre / retour d'une fonction (puisqu'il n'y a pas de tel type global). Pour ce faire, on utilisera std::function qui peut encapsuler une lambda, mais aussi d'autre objets appelables : ● Un foncteur ● Un pointeur de fonction « à la C » ● Un objet fonction créé avec std::bind #include <functional> void call(std::function<void(void)> f) { f(); } call([] { std::exit(1); }); call(std::bind(&std::exit, 1));
  • 13. Durée de vie Les fermetures lexicales peuvent « survivre » aux fonctions qui les ont créées : int a = 1; std::function<int(int)> returnClosure() { return [](int x) { return (x + a); }; } int main() { auto f = returnClosure(); std::cout << f(1); // affiche 2 a += 1; std::cout << f(1); // affiche 3 }
  • 14. Types de retour des lambdas Préciser le type de retour est optionnel quand : ● il s'agit de void ● le corps de la fonction lambda consiste en un return expr; Autrement le type de retour doit être spécifié via la syntaxe « à la traîne » (trailing return type) : auto f = [](int i) -> int { g(); return i + h(); }; C++14 assouplit les règles à ce niveau.
  • 15. Trailing return type notation Cette syntaxe à la traîne : ● Est la seule façon de préciser le type de retour des lambdas quand cela est nécessaire ● est permise pour n'importe quelle fonction (précédée de auto), y compris main() ● se combine souvent avec decltype void f(int x); // syntaxe traditionnelle auto f(int x)->void; // déclaration équivalente class A { public: bool f1() const; auto f2() const -> bool; };
  • 16. Capture de variables Pour référencer des variables locales (non statiques), la lambda doit les capturer (principe de la closure) : std::vector<int> v = { 5, 10, 20 }; int minVal = 10; // capture de minVal auto l = [minVal](int i) { return i > minVal; }; // affiche 20 std::cout << *std::find_if( class Lambda { public: Lambda(int m) : minVal(m) {} bool operator()(int i) const { return i > minVal; } private: int minVal; }; v.cbegin(), v.cend(), l); C++11 : le type capturé doit être copiable (donc pas de unique_ptr) C++14 introduit la généralisation de capture
  • 17. Capture de variables La capture peut aussi être effectuée par référence : auto l = [&minVal](int i) { return i > minVal; }; class Lambda { public: Lambda(int m) : minVal(m) {} bool operator()(int i) const { return i > minVal; } private: int & minVal; };
  • 18. Généralités On peut combiner les types de capture : int minVal = 10; int maxVal = 20; auto l = [&minVal, maxVal](int i) { return i > minVal && i < maxVal; }; class Lambda { public: Lambda(int m1, int m2) : minVal(m1), maxVal(m2) {} bool operator()(int i) const { return i > minVal && i < maxVal; } private: int & minVal; int maxVal; };
  • 19. Généralités Le mode de capture par défaut peut aussi être spécifié : int minVal = 10; int maxVal = 20; auto f1 = [=](int i) { // défaut : par valeur return i > minVal && i < maxVal; }; auto f2 = [&](int i) { // défaut : par référence return i > minVal && i < maxVal; }; Quand un mode de capture par défaut est spécifié, les variables capturées n'ont plus besoin d'être listées.
  • 20. Généralités On peut bien sûr ajuster le mode de capture au besoin : int minVal = 10; int maxVal = 20; auto f = [=, &minVal](int i) { return i > minVal && i < maxVal; }; minVal est capturé par référence, maxVal par valeur.
  • 21. Capturer des membres de classe On ne peut pas capturer directement les membres d'une classe : class A { public: void f() { // erreur: this->minVal ne peut pas être capturé ! auto l = [minVal](int i) { return i > minVal; }; } private: std::vector<int> data; int minVal; };
  • 22. Généralités Pour accéder aux membres d'une classe, il faut capturer this : class A { public: void f() { /// OK: "minVal" => "this->minVal" auto l = [this](int i) { return i > minVal; }; } private: std::vector<int> data; int minVal; }; Tous les membres de la classe (même privés) sont accessibles car le type de la closure fait partie intégrante de la classe où il a été défini.
  • 23. Capture implicite de this On peut aussi préciser un mode de capture par défaut afin de capturer implicitement this : class A { void f() { auto it = std::find_if(data.cbegin(), data.cend(), // OK: copie this dans la closure [=](int i) { return i > minVal; } ); } int minVal = 0; std::vector<int> data; };
  • 24. Capture de this par référence Version avec capture implicite par référence : void A::f() { auto it = std::find_if(data.cbegin(), data.cend(), // OK: maintient une référence vers this dans la closure [&](int i) { return i > minVal; } ); } A noter que : ● la capture de this par référence est potentiellement plus lente à cause de la double indirection (reference->this->minVal). ● comme toute référence, l'objet référencé peut ne plus exister… ● de même que la capture de this !
  • 25. Capture de this par référence Si un objet est capturé par référence, celui-ci peut être modifié : int n = 10; auto f = [&n] { n = 20; // OK }; struct Lambda1 { Lambda1(int & N) : n(N) {} void operator()() const { n = 20; // OK (bien que fonction const!) } int & n; }; Et oui : c'est l'objet référencé qui est modifié, pas la référence !
  • 26. Capture de this par référence Par contre, cela ne fonctionne pas avec une capture par copie : int n = 10; auto f = [n] { n = 20; // « impossible de modifier une capture par valeur dans une expression lambda non mutable » }; struct Lambda1 { Lambda1(int N) : n(N) {} void operator()() const { n = 20; // Erreur : modification depuis const ! } int n; }; Une lambda devrait en effet produire le même résultat si appelée deux fois de suite avec les mêmes arguments (stateless).
  • 27. Lambda mutable Pour pouvoir modifier une variable capturée par copie, il faut que la lambda soit mutable : int n = 10; auto f = [n]() mutable { n = 20; // OK }; struct Lambda1 { Lambda1(int N) : n(N) {} void operator()() { n = 20; } int n; }; operator() n'est plus const.
  • 28. Lambdas en C++14 C++14 vient compléter C++11 à divers niveaux. En ce qui concerne les lambdas, la modification majeure est la possibilité d'utiliser auto comme type des paramètres. Les lambdas deviennent alors génériques (polymorphiques).
  • 29. Lambda générique auto add = [](auto a, auto b) { return a + b; } struct Lambda { template<typename T1, typename T2> auto operator()(T1 a, T2 b) const -> decltype(a + b) { return a + b; Lambda = foncteur sous stéroïde ? } };
  • 30. Risques / abus d'utilisation ? Prepare for unforeseen consequences...
  • 31. Lambda vs fonction nommée auto isValidId = [](QString s) { return s.size() >= 4 && s.size() <= 8) && (s.toUpper() == s); }; for (auto & item : group1){ if (isValidId(item->id)) // ... } for (auto & item : group2) { if (isValidId(item->id)) // ... } static bool isValidId(QString s) { return s.size() >= 4 && s.size() <= 8) && (s.toUpper() == s); }; for (auto & item : group1){ if (isValidId(item->id)) // ... } for (auto & item : group2) { if (isValidId(item->id)) // ... } Si une lambda doit être utilisée plusieurs fois, faut-il lui préférer une fonction nommée (locale) ?
  • 32. Code déstabilisant à lire class ScopeGuard { public: ScopeGuard(std::function<void(void)> F) : f(F) {} ~ScopeGuard() { f(); } std::function<void(void)> f; }; int main() { FILE * file = nullptr; ScopeGuard guard([&file] { if (file != nullptr) { fclose(file); } }); // ... file = fopen("test.txt", "r"); }
  • 33. Attention à la capture par référence class A { public: int compute(); // résultat long à calculer }; future<int> computeAsync(shared_ptr<A> pA) { return async([&pA]() { return pA->compute(); }); } int main() { auto f = computeAsync(make_shared<A>()); // ... cout << f.get(); } Le pointeur intelligent reçu a été capturé sous forme de référence… son compteur d'utilisation n'est pas incrémenté ! Ce pointeur intelligent est un temporaire qui est détruit une fois la fonction computeAsync() appelée.
  • 34. Récapitulatif Les expressions lambda génèrent des fermetures lexicales (closures). Le contexte d'appel peut être capturé par valeur ou par référence. Le type de retour - si spécifié - utilise la syntaxe dite « à la traîne ». Les fermetures peuvent être conservées avec auto ou std::function. ● Attention à la durée de vie des variables capturées ! Les lambdas devraient rester concises et spécifiques à un contexte particulier (utilisées à un seul endroit). C++14 ajoute le support de paramètres auto, de la capture généralisée, ainsi que plus de souplesse au niveau de la déduction du type de retour.
  • 35. Conclusion Au final, une lambda c'est quoi ? Du sucre syntactique de foncteur sous stéroïde !