C++ 11/14

467 vues

Publié le

Un tour d'horizon des nouveautés du C++ 11/14

Publié dans : Logiciels
0 commentaire
1 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
467
Sur SlideShare
0
Issues des intégrations
0
Intégrations
29
Actions
Partages
0
Téléchargements
15
Commentaires
0
J’aime
1
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

C++ 11/14

  1. 1. Alexandre Hamez alexandre.hamez@gmail.com C++ 11/14
  2. 2. PRÉSENTATION GÉNÉRALE
  3. 3. C++11/14 3 • Nouveau langage • Possiblement des gains de performance juste en recompilant • rvalues references, move semantics • Quelques incompatibilités avec le standard précédent, mais bien souvent pour plus de sécurité ou de performances • ex. un destructeur ne peut plus lever d’exception par défaut (ce qui était déjà une mauvaise chose) • ex. règles de conversion plus strictes dans l’initialisation d’agrégats int tab[] = {1.0}; // compilation error
  4. 4. 4 ranged-base for loop auto constexpr static_assert rvalues references enum class uniform initialization noexcept variadic templates relaxed POD definition nullptr unrestricted unions lambdas decltype trailing return type extern template final override >> user-defined literals alignas alignof
  5. 5. 5 shared_ptr unordered_map mutex thread atomic regex unique_ptr bind reference_wrapper unordered_set tuple chrono random forward forward_list function enable_if result_of move array ...
  6. 6. Compilateurs 6 • Visual Studio 2013 (incomplet) ‣ http://msdn.microsoft.com/en-us/library/hh567368.aspx ‣ plus gros manque : constexpr • Clang ≥ 3.3 ‣ http://clang.llvm.org/cxx_status.html • GCC ≥ 4.8 ‣ https://gcc.gnu.org/projects/cxx0x.html
  7. 7. 7 http://en.cppreference.com
  8. 8. C++11/14
  9. 9. Boucles simplifiées 9 #include <iostream> #include <vector> int main() { std::vector<int> v; v.push_back(1); v.push_back(2); for ( std::vector<int>::const_iterator cit = v.begin() ; cit != v.end(); ++cit) { std::cout << *cit << ','; } } À l’ancienne : utilisation des itérateurs
  10. 10. Boucles simplifiées 10 #include <iostream> #include <vector> int main() { std::vector<int> v; v.push_back(1); v.push_back(2); for (int i : v) { std::cout << i << ','; } } Syntaxe simplifiée
  11. 11. Boucles simplifiées 10 #include <iostream> #include <vector> int main() { std::vector<int> v; v.push_back(1); v.push_back(2); for (int i : v) { std::cout << i << ','; } } Syntaxe simplifiée écriture naturelle
  12. 12. Boucles simplifiées 11 { typedef std::vector<int>::const_iterator cit_t; for ( cit_t __begin = v.begin(), __end = v.end() ; __begin != __end; ++__begin) { int i = *__begin; std::cout << i << ','; } } sous le capot :
  13. 13. Boucles simplifiées 12 std::map<int, int> m; m.insert(std::make_pair(1,1)); for (std::map<int, int>::value_type key_value : m) { std::cout << key_value.first << ',' << key_value.second; } n’importe quel conteneur fournissant des itérateurs
  14. 14. Boucles simplifiées 12 std::map<int, int> m; m.insert(std::make_pair(1,1)); for (std::map<int, int>::value_type key_value : m) { std::cout << key_value.first << ',' << key_value.second; } n’importe quel conteneur fournissant des itérateurs pas de ‘&’ →copie
  15. 15. Boucles simplifiées 13 const int tab[] = {1,2,3}; for (int x : tab) { std::cout << x << 'n'; } tableaux C
  16. 16. Boucles simplifiées 14 • En résumé, fonctionne pour ‣ les tableaux C ‣ tous les types qui fournissent - les fonctions membres begin(), end() - les fonctions libres begin(type), end(type)
  17. 17. auto 15 #include <map> int main() { std::map<int, int> m; m.insert(std::make_pair(1,1)); for (std::pair<int, int>& key_value : m) { // do something with key_value } } où est le problème?
  18. 18. auto 16 #include <map> int main() { std::map<int, int> m; m.insert(std::make_pair(1,1)); for (std::pair<const int, int>& key_value : m) { // ... } } où est le problème?
  19. 19. auto 16 #include <map> int main() { std::map<int, int> m; m.insert(std::make_pair(1,1)); for (std::pair<const int, int>& key_value : m) { // ... } } où est le problème? la clef dans une std::map est constante
  20. 20. auto 17 #include <map> int main() { std::map<int, int> m; m.insert(std::make_pair(1,1)); for (auto& key_value : m) { // ... } }
  21. 21. auto 17 #include <map> int main() { std::map<int, int> m; m.insert(std::make_pair(1,1)); for (auto& key_value : m) { // ... } } le compilateur fait le travail pour nous : plus d’erreur possible
  22. 22. 18 auto #include <map> int main() { auto m = std::map<int, int>(); m.insert(std::make_pair(1,1)); for (auto& key_value : m) { // do something with key_value } }
  23. 23. 18 auto #include <map> int main() { auto m = std::map<int, int>(); m.insert(std::make_pair(1,1)); for (auto& key_value : m) { // do something with key_value } } pour toute variable
  24. 24. 19 auto m = std::map<int, int>(); for (const auto& key_value : m) { key_value.second = 2; // compilation error } auto auto utilise les règles de déduction des templates
  25. 25. 20 #include <vector> std::vector<int> fun() { // return something } int main() { const auto container = fun(); for (const auto& value : container) { // do something } } auto facilite la maintenance de code
  26. 26. 21 #include <deque> std::deque<int> fun() { // return something } int main() { const auto container = fun(); for (const auto& value : container) { // do something } } facilite la maintenance de code auto
  27. 27. 21 #include <deque> std::deque<int> fun() { // return something } int main() { const auto container = fun(); for (const auto& value : container) { // do something } } fun() change facilite la maintenance de code auto
  28. 28. 21 #include <deque> std::deque<int> fun() { // return something } int main() { const auto container = fun(); for (const auto& value : container) { // do something } } main() ne change pas! fun() change facilite la maintenance de code auto
  29. 29. 22 • permet d’adopter un style de programmation fonctionnelle ‣ Scala : val x = fun() ‣ C++ : const auto x = fun(); • auto utilise les règles de déduction des templates ‣ sauf pour le cas des listes d’initialisation (initializer_list) auto
  30. 30. 23 const auto x = foo(); void* _ = x; error: cannot initialize a variable of type 'void *' with an lvalue of type 'TYPE'   void* _ = x;         ^   ~ 1 error generated. auto connaître le type déduit (tout compilateur)
  31. 31. Initialisation uniformisée 24 struct foo {}; struct bar { bar(foo){} }; int main() { // function declaration or variable definition? bar b(foo()); } “most vexing parse” • define variable b of type bar with an instance of foo; or • declare function b which returns a bar and takes a function with no parameter and which returns a foo
  32. 32. 25 Initialisation uniformisée struct foo {}; struct bar { bar(foo){} }; int main() { // add parentheses to declare a variable bar b((foo())); } “most vexing parse”
  33. 33. 26 struct foo {}; struct bar { bar(foo){} }; int main() { // brace initializer bar b{foo{}}; // better auto b1 = bar{foo{}}; } solution : utiliser la syntaxe pour initialiser les agrégats : {} Initialisation uniformisée
  34. 34. 27 struct foo { int i_; foo(int i) : i_(i) {} }; struct bar { int i_; }; int main() { const auto f = foo{42}; const auto b = bar{42}; } pour construire un objet Initialisation uniformisée
  35. 35. 28 class foo { int i_; public: foo(int i) : i_(i) {} }; struct bar { int i_; }; int main() { const auto f = foo{42}; const auto b = bar{42}; } pour construire struct ou class Initialisation uniformisée
  36. 36. 29 struct foo { int i; }; foo fun() { return {42}; } int main() { const auto f = fun(); } concision : on peut omettre le type de retour Initialisation uniformisée
  37. 37. 29 struct foo { int i; }; foo fun() { return {42}; } int main() { const auto f = fun(); } concision : on peut omettre le type de retour pas de type explicite Initialisation uniformisée
  38. 38. 30 #include <map> int main() { auto m = std::map<int,int>{}; // old style m.insert(std::make_pair(1, 2)); // new style m.insert({1, 2}); } exemple : insertion dans un conteneur associatif Initialisation uniformisée
  39. 39. 31 double a, b, c; int x{a + b + c}; int y(a + b + c); int z = a + b + c; ne compile pas : perte de précision OK (mais mal) idem plus de sécurité Initialisation uniformisée
  40. 40. 32 • Que choisir? • “{}” ‣ résistant au most vexing parse ‣ empêche la perte de précision ‣ utilisation plus cohérente • “()” ‣ ne casse pas les habitudes ‣ pas de confusion avec initializer_list (à suivre…) Initialisation uniformisée
  41. 41. initializer_list 33 #include <iostream> #include <vector> int main() { const auto vec = std::vector<int>{1,2,3}; for (const auto& x : vec) { std::cout << x; } } même syntaxe que pour initialiser les agrégats
  42. 42. initializer_list 33 #include <iostream> #include <vector> int main() { const auto vec = std::vector<int>{1,2,3}; for (const auto& x : vec) { std::cout << x; } } déclaration “naturelle” même syntaxe que pour initialiser les agrégats
  43. 43. initializer_list 34 • C++11 définit un nouveau type ‣ std::initializer_list<T> ‣ #include <initializer_list> • Utilise la même syntaxe d'initialisation des tableaux C • {1,2,3} • Le contenu de la liste est copié
  44. 44. initializer_list 35 #include <initializer_list> int main() { // deduced to std::initializer_list<int> const auto list = {1,2,2}; for (auto x : list) { // ... } } exception : auto + {} initializer_list auto utilise la déduction des templates
  45. 45. initializer_list 35 #include <initializer_list> int main() { // deduced to std::initializer_list<int> const auto list = {1,2,2}; for (auto x : list) { // ... } } ne pas oublier! exception : auto + {} initializer_list auto utilise la déduction des templates
  46. 46. initializer_list 36 void foo(std::initializer_list<int> list) { for (auto i : list) {} } foo({1,2,3}); initializer_list en paramètre
  47. 47. initializer_list 36 void foo(std::initializer_list<int> list) { for (auto i : list) {} } foo({1,2,3}); passage par valeur initializer_list en paramètre
  48. 48. initializer_list 37 struct bar { bar(std::initializer_list<int> list); }; const auto b = bar{1,2,3}; can be used to construct objects
  49. 49. initializer_list 38 void foo(int i, std::initializer_list<int> li) { std::cout << i << " : "; for (auto x : li) std::cout << x << ","; } foo(42, {3,2,3,4}); utilisable avec d’autres arguments
  50. 50. initializer_list 39 for (auto x : {1,2,3,4}) { std::cout << x << ','; } utilisable dans une boucle simplifiée
  51. 51. initializer_list 40 #include <iostream> #include <initializer_list> struct bar { bar(std::initializer_list<int> list){std::cout << "listn";} bar(int) {std::cout << "intn";} }; int main() { const auto b = bar{1}; } qu’affiche le code suivant?
  52. 52. initializer_list 41 #include <iostream> #include <vector> int main () { const auto v1 = std::vector<int>{2}; std::cout << v1.size(); const auto v2 = std::vector<int>(2); std::cout << v2.size(); } mais… qu’affiche le code suivant?
  53. 53. initializer_list 42 • Permet de ‣ faciliter l’écriture des tests unitaires ‣ offrir une API élégante au code client ‣ … const auto v1 = std::vector<int>{1,2,3}; auto v2 = std::vector<int>{}; v2.reserve(3); v2.push_back(1); v2.push_back(2); v2.push_back(3); vs
  54. 54. initializer_list 42 • Permet de ‣ faciliter l’écriture des tests unitaires ‣ offrir une API élégante au code client ‣ … const auto v1 = std::vector<int>{1,2,3}; auto v2 = std::vector<int>{}; v2.reserve(3); v2.push_back(1); v2.push_back(2); v2.push_back(3); vs bonus : plus d’immutabilité
  55. 55. User-defined literals 43 unsigned long x0 = 0ul; long x1 = 0l; // etc. C++03 : il est possible de suffixer un littéral pour y appliquer des caractéristiques
  56. 56. User-defined literals 44 long double operator"" _deg (long double x) { return x * 3.141592/180; } const auto x = 90.0_deg; pour convertir une valeur
  57. 57. User-defined literals 44 long double operator"" _deg (long double x) { return x * 3.141592/180; } const auto x = 90.0_deg; ne pas oublier “_” pour convertir une valeur
  58. 58. User-defined literals 45 struct foo { const unsigned long long x_; foo(unsigned long long x) : x_(x) {} }; foo operator"" _foo (unsigned long long n) { return {n}; } const auto f = 123_foo; pour construire un objet
  59. 59. User-defined literals 46 void operator"" _print(const char* str, std::size_t) { std::cout << str; } "C++11 rocks!"_print; à partir de chaînes de caractères
  60. 60. User-defined literals 47 struct point { //??? }; // Yaye! const auto p = 3_x + 5_y; comment obtenir cette construction?
  61. 61. User-defined literals 48 struct x_type{unsigned long long value;}; struct y_type{unsigned long long value;}; x_type operator"" _x(unsigned long long value) {return {value};} y_type operator"" _y(unsigned long long value) {return {value};} struct point { unsigned long long x_, y_; private: point(const x_type& x, const y_type& y) : x_(x.value), y_(y.value) {} friend point operator+(const x_type&, const y_type&); }; point operator+(const x_type& x, const y_type& y) {return {x, y};}; // later... const auto p = 3_x + 5_y;
  62. 62. String literals 49 const auto utf8 = u8"UTF-8 string. u2705n”; std::cout << utf8; const auto utf16 = u8"UTF-16 string.n”; std::wcout << utf16; UTF-8 string. ✅ UTF-16 string. Gestion UTF-8, UTF-16 et UTF-32
  63. 63. Binary literals 50 const auto b = 0b00101010; std::cout << b; // 42 C++14 uniquement
  64. 64. Séparateur ‘ 51 const auto big_value = 64'000'000'000'000'000; facilite la lecture des grandes constantes const auto b = 0b0010'1010;
  65. 65. Raw string literals 52 const auto raw = R"(@ tLook Ma, no tabs! some text )"; const auto escaped = "@n" "somen" "textn"; int main() { std::cout << raw; std::cout << "--------------n"; std::cout << escaped; } @ tLook Ma, no tabs! some text -------------- @ some text
  66. 66. enum class 53 int main() { enum e{pink, white, orange, brown}; const auto white = 0ul; } error: redefinition of 'white' as different kind of symbol   const auto white = 0ul;              ^ note: previous definition is here   enum e{pink, white, orange, brown};                ^ enum C++03 : pollution de l’espace de nommage
  67. 67. enum class 53 int main() { enum e{pink, white, orange, brown}; const auto white = 0ul; } ne compile pas error: redefinition of 'white' as different kind of symbol   const auto white = 0ul;              ^ note: previous definition is here   enum e{pink, white, orange, brown};                ^ enum C++03 : pollution de l’espace de nommage
  68. 68. enum class 54 enum e {pink, white, orange, brown}; if (pink < 99.3) { // ... } enum C++03 : conversion implicite dangereuse
  69. 69. enum class 54 enum e {pink, white, orange, brown}; if (pink < 99.3) { // ... } enum C++03 : conversion implicite dangereuse compile, mais est-ce vraiment une bonne chose?
  70. 70. enum class 55 int main() { enum class e{pink, white, orange, brown}; const auto white = 0ul; const auto e0 = e::white; } enum class créé une nouvelle portée
  71. 71. enum class 55 int main() { enum class e{pink, white, orange, brown}; const auto white = 0ul; const auto e0 = e::white; } enum class créé une nouvelle portée accès à la portée de e
  72. 72. enum class 56 enum class e{pink, white, orange, brown}; // use std::underlying_type<e>::type to find enum’s type type par défaut sous-jacent : int
  73. 73. enum class 57 enum class e : unsigned long {pink = 0, white, orange, brown = 0xFFFFFFFFFFFFFFFF}; // use std::underlying_type<e>::type to find enum’s type changer le type sous-jacent
  74. 74. enum class 58 • C++03 : il n’y a pas de type par défaut pour énumération ‣ on est donc obligé de la mettre dans les headers ‣ recompilation en cascade s’il faut rajouter un champ • C++11 : on peut faire une pré-déclaration ‣ définition dans une unité de compilation séparée ‣ pas de recompilation en cascade
  75. 75. nullptr 59 void f(int) {std::cout << "f(int)";} void f(void*){std::cout << "f(void*)";} f(0); f(NULL); error: call to 'f' is ambiguous   f(NULL);   ^ note: candidate function void f(int)  {std::cout << "f(int)";}      ^ note: candidate function void f(void*){std::cout << "f(void*)";} f(int) f(int) clang/gcc Visual Studio
  76. 76. nullptr 60 void f(int) {std::cout << "f(int)";} void f(void*){std::cout << "f(void*)";} f(0); f(nullptr); f(int) f(void*) moins d’ambiguité
  77. 77. nullptr 61 const auto x = f(); if (x == 0) { // ... } plus de clarté const auto x = f(); if (x == nullptr) { // ... } vs
  78. 78. nullptr 61 const auto x = f(); if (x == 0) { // ... } plus de clarté const auto x = f(); if (x == nullptr) { // ... } vs intention plus claire
  79. 79. nullptr 62 type de nullptr void f(int) {std::cout << "f(int)";} void f(void*) {std::cout << "f(void*)";} void f(std::nullptr_t) {std::cout << "f(nullptr_t)";} f(nullptr);
  80. 80. alignas 63 alignas(16) int a; alignas(32768) int b; std::cout << &a; std::cout << &b; 0x7fff58d2ffe0 0x7fff58d28000 contrôler l’alignement mémoire
  81. 81. alignof 64 std::cout << alignof(int); std::cout << alignof(long); std::cout << alignof(char); std::cout << alignof(char*); connaître l’alignement mémoire (d’un type) 4 8 1 8
  82. 82. Classes • Délégation de constructeurs • Héritage des constructeurs des classes de base • Initialisation directe des attributs • Contrôle de l’héritage • Contrôle explicite des méthodes supprimées 65
  83. 83. Classes 66 délégation de constructeurs (1) struct foo { const int x_; foo(int x) : x_{x} {} foo() : foo{42} {} };
  84. 84. Classes 66 délégation de constructeurs (1) struct foo { const int x_; foo(int x) : x_{x} {} foo() : foo{42} {} }; délégation
  85. 85. Classes 67 délégation de constructeurs (2) struct foo { const int x_; foo(int x = 42) : x_{x} {} }; on pourrait utiliser les arguments par défaut problème : dans l’interface de la classe
  86. 86. Classes 68 héritage des constructeurs (1) : C++03 struct base { const int x_; base(int x) : x_(x) {} }; struct foo : public base { foo(int x) : base(x) {} };
  87. 87. Classes 68 héritage des constructeurs (1) : C++03 struct base { const int x_; base(int x) : x_(x) {} }; struct foo : public base { foo(int x) : base(x) {} }; appel explicite
  88. 88. Classes 69 héritage des constructeurs (2) : C++11 struct base { const int x_; base(int x) : x_(x) {} }; struct foo : public base { using base::base; };
  89. 89. Classes 69 héritage des constructeurs (2) : C++11 struct base { const int x_; base(int x) : x_(x) {} }; struct foo : public base { using base::base; }; hérite de tous les constructeurs de base
  90. 90. Classes 70 initialisation directe des attributs struct foo { const int x_ = 33; foo(int x) : x_(x) {} foo() {} };
  91. 91. Classes 70 initialisation directe des attributs struct foo { const int x_ = 33; foo(int x) : x_(x) {} foo() {} }; valeur par défaut utilisée par tous les constructeurs
  92. 92. Classes 71 contrôle de l’héritage avec final struct foo final {}; struct bar : foo {};
  93. 93. Classes 71 contrôle de l’héritage avec final struct foo final {}; struct bar : foo {}; ne compile pas error: base 'foo' is marked 'final' struct bar : foo {};              ^
  94. 94. Classes 72 contrôle de l’héritage avec override (1) struct base { virtual void operator()(float x) const {std::cout << "base " << x;} }; struct derived : public base { virtual void operator()(int x) const {std::cout << "derived " << x;} }; const auto d = derived{}; auto x = 33.3; d(x);
  95. 95. Classes 72 contrôle de l’héritage avec override (1) struct base { virtual void operator()(float x) const {std::cout << "base " << x;} }; struct derived : public base { virtual void operator()(int x) const {std::cout << "derived " << x;} }; const auto d = derived{}; auto x = 33.3; d(x); affichage?
  96. 96. Classes 73 contrôle de l’héritage avec override (2) struct base { virtual void operator()(float x) const; }; struct derived : public base { virtual void operator()(int x) const override; };
  97. 97. Classes 73 contrôle de l’héritage avec override (2) struct base { virtual void operator()(float x) const; }; struct derived : public base { virtual void operator()(int x) const override; }; error: 'operator()' marked 'override' but does not override any member functions   virtual void operator()(int x) const                ^
  98. 98. Classes 74 contrôle des fonctions membres avec default / delete (1) struct non_copyable { non_copyable(){} private: non_copyable(const non_copyable&); non_copyable& operator=(const non_copyable&); }; int main() { const auto nc = non_copyable{}; } error: calling a private constructor of class 'non_copyable'   const auto nc = non_copyable{};                   ^
  99. 99. Classes 75 struct non_copyable { non_copyable() = default; non_copyable(const non_copyable&) = delete; non_copyable& operator=(const non_copyable&) = delete; }; int main() { const auto nc = non_copyable{}; } error: call to deleted constructor of 'const non_copyable'   const auto nc = non_copyable{};              ^    ~~~~~~~~~~~~~~ note: 'non_copyable' has been explicitly marked deleted here   non_copyable(const non_copyable&) = delete; contrôle des fonctions membres avec default / delete (2)
  100. 100. Classes 76 struct base { virtual void operator()(int x) const; }; struct derived : public base { virtual void operator()(int x) const override; }; int main() { const auto override = 0ul; const auto final = 0ul; } contrôle des fonctions membres avec default / delete (3)
  101. 101. Classes 76 struct base { virtual void operator()(int x) const; }; struct derived : public base { virtual void operator()(int x) const override; }; int main() { const auto override = 0ul; const auto final = 0ul; } pas des mots-clefs contrôle des fonctions membres avec default / delete (3)
  102. 102. delete 77 void foo(int){}; void foo(long) = delete; void foo(double) = delete; foo(42); // OK foo(42l); // ERROR foo(42.0); // ERROR permet aussi d’empêcher certaines surcharges error: call to deleted function 'foo'   foo(42l);   ^~~ note: candidate function has been explicitly deleted void foo(long) = delete;      ^ note: candidate function void foo(int){};      ^ note: candidate function has been explicitly deleted void foo(double) = delete;      ^
  103. 103. Rvalues references • Ajout majeur au C++ • Travailler explicitement avec les temporaires • Éviter des copies inutiles ‣ move semantics • Perfect forwarding ‣ construire en place ‣ généricité améliorée ‣ … 78
  104. 104. Rvalues references • lvalues ‣ un objet qui occupe une place identifiable en mémoire - qui a une adresse • rvalues ‣ un objet qui n’occupe pas une place identifiable en mémoire - qui n’a pas d’adresse : temporaire 79 lvalue = rvalue lvalue = lvalue rvalue = lvalue int x = 42 int x = y 42 = ...
  105. 105. Rvalues references 80 int foo() {return 2;} int main() { foo() = 2; } error: lvalue required as left operand of assignment foo() = 2; ^
  106. 106. Rvalues references 81 struct foo { foo() { std::cout << “foo()n”; } foo(const foo&) { std::cout << "foo(const foo&)n”; } foo& operator=(const foo&) { std::cout << "operator=(const foo&)n”; return *this; } }; foo bar() {return {};} const auto f0 = foo{}; auto f1 = bar(); f1 = bar(); interlude : copy elision
  107. 107. Rvalues references 81 struct foo { foo() { std::cout << “foo()n”; } foo(const foo&) { std::cout << "foo(const foo&)n”; } foo& operator=(const foo&) { std::cout << "operator=(const foo&)n”; return *this; } }; foo bar() {return {};} const auto f0 = foo{}; auto f1 = bar(); f1 = bar(); interlude : copy elision ?
  108. 108. Rvalues references 82 comment éviter la copie? (1) foo* bar() {return new foo{};} auto f1 = bar(); // later... f1 = bar(); • Retour par pointeur ‣ Coût de l’allocation ‣ Qui est responsable du pointeur?
  109. 109. Rvalues references 83 comment éviter la copie? (2) void bar(foo*); auto f = foo{}; bar(&f); • Passage par pointeur ou référence en paramètre • Quid des factories?
  110. 110. Rvalues references 84 comment modifier un temporaire? void bar(foo&); //... auto f = foo{}; bar(f1); bar(foo{}); utile pour un objet fonction avec état (cache, etc.)
  111. 111. Rvalues references 84 comment modifier un temporaire? void bar(foo&); //... auto f = foo{}; bar(f1); bar(foo{}); ne compile pas utile pour un objet fonction avec état (cache, etc.)
  112. 112. Rvalues references 85 struct foo{int x;}; void bar(foo&& f) { std::cout << f.x << 'n'; } //... bar(foo{42});
  113. 113. Rvalues references 85 struct foo{int x;}; void bar(foo&& f) { std::cout << f.x << 'n'; } //... bar(foo{42}); foo&& : rvalue reference
  114. 114. Rvalues references 85 struct foo{int x;}; void bar(foo&& f) { std::cout << f.x << 'n'; } //... bar(foo{42}); foo&& : rvalue reference foo{42} : rvalue expression
  115. 115. Rvalues references 85 struct foo{int x;}; void bar(foo&& f) { std::cout << f.x << 'n'; } //... bar(foo{42}); foo&& : rvalue reference foo{42} : rvalue expression f : lvalue expression on peut prendre l’adresse de f
  116. 116. Rvalues references 85 struct foo{int x;}; void bar(foo&& f) { std::cout << f.x << 'n'; } //... bar(foo{42}); foo&& : rvalue reference foo{42} : rvalue expression f : lvalue expression on peut prendre l’adresse de f le passage en paramètre “nomme” le temporaire celui-ci devient une lvalue : on peut prendre son adresse sur la pile
  117. 117. Rvalues references 86 une rvalue reference n’accepte que des rvalue expressions struct foo{int x;}; void bar(foo&& f) { std::cout << f.x << 'n'; } //... auto f = foo{42}; bar(f);
  118. 118. Rvalues references 86 une rvalue reference n’accepte que des rvalue expressions struct foo{int x;}; void bar(foo&& f) { std::cout << f.x << 'n'; } //... auto f = foo{42}; bar(f); erreur : f n’est pas une rvalue expression
  119. 119. Rvalues references 87 ref-qualifiers struct foo { void operator()() const && { std::cout << "&&n"; } void operator()() const & { std::cout << "&n"; } }; //... const auto f = foo{}; f(); foo{}();
  120. 120. Rvalues references 87 ref-qualifiers struct foo { void operator()() const && { std::cout << "&&n"; } void operator()() const & { std::cout << "&n"; } }; //... const auto f = foo{}; f(); foo{}(); en tant que rvalue en tant que lvalue
  121. 121. Move semantics • Permet d’exprimer le “déplacement” d’une ressource • Permet d’exprimer le changement de propriétaire d’une entité non copiable ‣ mutex, fichier, … • Utilisation des rvalues references • Utilisation de std::move() ‣ instruction au compilateur ‣ ne génère aucun code 88
  122. 122. Ressource2Ressource1 Move semantics 89 A B
  123. 123. X Ressource1 Move semantics 89 A B
  124. 124. Move semantics 90 struct foo { some_type* ressource; foo& operator=(const foo& rhs) { // destroy this->ressource // clone rhs.ressource // attach this cloned ressource to this->ressource } }; auto f1 = foo{}; auto f2 = foo{}; // later... f2 = f1; copie
  125. 125. Move semantics 90 struct foo { some_type* ressource; foo& operator=(const foo& rhs) { // destroy this->ressource // clone rhs.ressource // attach this cloned ressource to this->ressource } }; auto f1 = foo{}; auto f2 = foo{}; // later... f2 = f1; copie O(n)
  126. 126. Move semantics 91 auto f1 = foo{}; //... auto f2 = std::move(f1); // now f1 is “invalid” transfert de ressource (1) move() transforme une lvalue expression en rvalue expression
  127. 127. Move semantics 92 struct foo { some_type* ressource; foo& operator=(foo&& rhs) { // destroy this->ressource // copy rhs.ressource pointer to this->ressource } }; auto f1 = foo{}; auto f2 = foo{}; //... f2 = std::move(f1); transfert de ressource (2)
  128. 128. Move semantics 92 struct foo { some_type* ressource; foo& operator=(foo&& rhs) { // destroy this->ressource // copy rhs.ressource pointer to this->ressource } }; auto f1 = foo{}; auto f2 = foo{}; //... f2 = std::move(f1); transfert de ressource (2) O(1)
  129. 129. Move semantics 92 struct foo { some_type* ressource; foo& operator=(foo&& rhs) { // destroy this->ressource // copy rhs.ressource pointer to this->ressource } }; auto f1 = foo{}; auto f2 = foo{}; //... f2 = std::move(f1); transfert de ressource (2) O(1) mais pas toujours…
  130. 130. Move semantics 93 struct foo { foo() = default; foo(const foo&) = default; foo(foo&&) = delete; }; int main() { const auto f = foo{}; }
  131. 131. Move semantics 93 struct foo { foo() = default; foo(const foo&) = default; foo(foo&&) = delete; }; int main() { const auto f = foo{}; } ne compile pas
  132. 132. Move semantics 94 struct foo {}; foo return_by_value() { const auto f = foo{}; return f; } void take_by_value(foo) {} auto f1 = foo{}; auto f2 = f1; auto f3 = std::move(f1); take_by_value(f2); take_by_value(std::move(f2)); auto f4 = return_by_value(); f1 = return_by_value(); quelles sont les opérations appelées?
  133. 133. Move semantics 95 transfert de propriété struct foo { some_type ressource; // forbid copy foo(const foo&) = delete; foo& operator=(const foo&) = delete; foo(foo&& rhs) noexcept { // 1. acquire ownership of rhs.ressource // 2. free rhs from its ownership } foo& operator=(foo&&) noexcept { // 1. release ownership of current ressource // 2. acquire ownership of rhs.ressource // 3. free rhs from its ownership } };
  134. 134. Move semantics 96 struct foo; foo bar() { auto f = foo{}; // later... return std::move(f); } std::move en retour d’une fonction
  135. 135. Move semantics 96 struct foo; foo bar() { auto f = foo{}; // later... return std::move(f); } mauvaise idée std::move en retour d’une fonction
  136. 136. Move semantics 97 const rvalue references? void bar(const foo&& f); // what for if we can't move f? void bar(const foo&); // read only réellement utile pour interdire tout usage des rvalues struct foo { foo() {}; foo(foo&&) = delete; foo(const foo&&) = delete; }; // later... const foo f0; foo f1(std::move(f0)); void bar(const foo&& f); // what for if we can't move f? void bar(const foo&); // read only
  137. 137. Move semantics 98 struct foo; foo f1; foo f2; std::swap(f1, f2); C++03 vs C++11 foo() foo() foo(foo&&) operator=(foo&&) operator=(foo&&) foo() foo() foo(const foo&) operator=(const foo&) operator=(const foo&) C++03 C++11
  138. 138. decltype 99 permet de connaître le type d’une expression int* foo(); using ty0 = decltype(foo); print_type<ty0>(); using ty1 = decltype(foo()); print_type<ty1>(); void print_type() [T = int *()] void print_type() [T = int *]
  139. 139. trailing return type 100 auto foo() -> int { return 42; } std::cout << foo(); type de retour après les paramètres
  140. 140. trailing return type 101 template <typename T1, typename T2> ??? add(const T1& x, const T2& y) { return x + y; } si le type de retour dépend des paramètres template <typename T1, typename T2> auto add(const T1& x, const T2& y) -> decltype(x+y) { return x + y; }
  141. 141. trailing return type 102 double (*get_fun2(bool arg))(double) { if (arg) return std::cos; else return std::sin; } plus de lisibilité auto get_fun(bool arg) -> double(*)(double) { if (arg) return std::cos; else return std::sin; } vs
  142. 142. trailing return type 103 C++14 : on peut se passer du type de retour template <typename T1, typename T2> auto add(const T1& x, const T2& y) -> decltype(x+y) { return x + y; }
  143. 143. trailing return type 104 C++14 : on peut se passer du type de retour mais pas toujours… auto get_fun(bool arg) { if (arg) return std::cos; else return std::sin; } error: cannot deduce return type 'auto' from returned value of type '<overloaded function type>'   if (arg) return std::cos;
  144. 144. 105 lambdas struct print1 { void operator()(int x) const { std::cout << x << 'n'; } }; void print2(int x) { std::cout << x << 'n'; } const auto v = {1,2,3}; std::for_each(begin(v), end(v), print1{}); std::for_each(begin(v), end(v), print2); programmation fonctionnelle : objets fonctions et fonctions en argument
  145. 145. lambdas 106 const auto v = {1,2,3}; std::for_each( begin(v), end(v) , [](int x){std::cout << x << ‘n';} ); const auto v = {1,2,3}; const auto print = [](int x){std::cout << x << 'n';}; std::for_each(begin(v), end(v), print); par l’exemple const auto fun = []{return 42;}; const auto x = fun(); std::cout << x << 'n';
  146. 146. lambdas 107 const auto print = [](int x){std::cout << x;}; print_type<decltype(print)>(); void print_type() [with T = const main()::<lambda(int)>] type d’un lambda il n’y pas de type pour les lambdas en tant que tel auto est quasi-obligatoire si on veut stocker un lambda ou presque…
  147. 147. lambdas 108 anatomie d’un lambda [capture](params) -> ret_type {body} comment “capturer” l’environnement corps de la fonction peut-être omis type de retour si nécessaire
  148. 148. lambdas 109 spécification du type de retour (1) struct foo {}; struct bar : public foo {}; struct baz : public foo {}; const auto foo_maker = [](bool value) { if (value) return new bar{}; else return new baz{}; }; const auto ptr = foo_maker(true); error: return type 'baz *' must match previous return type 'bar *' when lambda expression has unspecified explicit return type                              else       return new baz{};                                         ^
  149. 149. lambdas 110 spécification du type de retour (2) struct foo {}; struct bar : public foo {}; struct baz : public foo {}; const auto foo_maker = [](bool value) -> foo* { if (value) return new bar{}; else return new baz{}; }; const auto ptr = foo_maker(true);
  150. 150. lambdas 110 spécification du type de retour (2) struct foo {}; struct bar : public foo {}; struct baz : public foo {}; const auto foo_maker = [](bool value) -> foo* { if (value) return new bar{}; else return new baz{}; }; const auto ptr = foo_maker(true); type de retour
  151. 151. lambdas 111 capture de l’environnement (1) [] ne capture rien [&] tout par référence [=] tout par valeur [this] capture this [=,&b] copie tout, mais reférence b [&a, b] référence a, copie b environnement = toutes les variables de la portée englobante
  152. 152. lambdas 111 capture de l’environnement (1) [] ne capture rien [&] tout par référence [=] tout par valeur [this] capture this [=,&b] copie tout, mais reférence b [&a, b] référence a, copie b environnement en “lecture seule” environnement = toutes les variables de la portée englobante const auto x = 42u; []{std::cout << x;}(); // OK auto x = 40u; []{x += 2;}(); // ERROR
  153. 153. struct foo { int x_; int bar(int y) { return [this](int y) {return x_ + y;} (y); } }; std::cout << foo{40}.bar(2); lambdas 112 auto x = 40u; [&x](int y){x += y;}(2); std::cout << x; auto a = 40u; [&](int b){a += b;}(2); std::cout << x; capture de l’environnement (2)
  154. 154. struct foo { int x_; int bar(int y) { return [this](int y) {return x_ + y;} (y); } }; std::cout << foo{40}.bar(2); lambdas 112 auto x = 40u; [&x](int y){x += y;}(2); std::cout << x; auto a = 40u; [&](int b){a += b;}(2); std::cout << x; capture de l’environnement (2)
  155. 155. auto x = 44u; [=]{x -= 2;}(); lambdas 113 error: cannot assign to a variable captured by copy in a non-mutable lambda   [=]{x -= 2;}();       ~ ^ auto x = 44u; [=]() mutable {x -= 2;}(); capture de l’environnement (3)
  156. 156. auto x = 44u; [=]{x -= 2;}(); lambdas 113 error: cannot assign to a variable captured by copy in a non-mutable lambda   [=]{x -= 2;}();       ~ ^ auto x = 44u; [=]() mutable {x -= 2;}(); obligatoire capture de l’environnement (3)
  157. 157. lambdas 114 “fermeture” équivalente à un objet fonction (1) struct closure { double& capture_; int operator()(int x, int& y) const { return (x + y) * capture_; } closure() = delete; closure& operator=(const closure&) = delete; closure(const closure& ) = default; closure(closure&& ) = default; ~closure() = default; }; auto fun0 = [&d](int x, int& y){return (x + y) * d;};
  158. 158. lambdas 114 “fermeture” équivalente à un objet fonction (1) struct closure { double& capture_; int operator()(int x, int& y) const { return (x + y) * capture_; } closure() = delete; closure& operator=(const closure&) = delete; closure(const closure& ) = default; closure(closure&& ) = default; ~closure() = default; }; auto fun0 = [&d](int x, int& y){return (x + y) * d;}; d’où le mutable
  159. 159. lambdas 115 auto d = 0.0; auto fun1 = fun0; // OK fun1 = fun0; // KO no copy operator using ty = decltype(fun0); auto fun2 = ty{}; // KO no default ctor auto fun0 = [&d](int x, int& y){return (x + y) * d;}; “fermeture” équivalente à un objet fonction (2)
  160. 160. lambdas 116 en paramètre d’un template template <typename F, typename T> void apply(T x) { F{}(x); } auto fun = [](int x){x += 1;}; apply<decltype(fun)>(0);
  161. 161. lambdas 117 attention aux références invalides auto mk_fun() -> std::function<void (int&)> { auto i = 33u; return [&](int& j){j += i;}; } int main() { auto j = 2; const auto fun = mk_fun(); fun(j); // Doomed to failure }
  162. 162. lambdas 117 attention aux références invalides auto mk_fun() -> std::function<void (int&)> { auto i = 33u; return [&](int& j){j += i;}; } int main() { auto j = 2; const auto fun = mk_fun(); fun(j); // Doomed to failure } portée locale à mk_fun
  163. 163. lambdas 117 attention aux références invalides auto mk_fun() -> std::function<void (int&)> { auto i = 33u; return [&](int& j){j += i;}; } int main() { auto j = 2; const auto fun = mk_fun(); fun(j); // Doomed to failure } portée locale à mk_fun lecture de i locale à mk_fun
  164. 164. lambdas 118 C++14 : paramètres génériques const auto add = [](const auto& x, const auto& y){return x + y;}; add(1,2); add(std::string{"1"}, std::string{"2"});
  165. 165. lambdas 119 au service de l’immutabilité const auto read_only = [&] { std::vector<int> res; // super-complex initialisation return res }(); read_only.push_back(3); // nope, won’t do auto read_only = std::vector<int>{}; // super-complex initialisation // ...later // damned, still mutable! read_only.push_back(3);
  166. 166. noexcept • C++03 ‣ il faut lister dans la signature d’une fonction les exceptions qu’elle peut lever (throw (…)) ‣ difficile à maintenir ‣ pas ou peu d’aide des compilateurs • C++11 ‣ seule alternative : une fonction peut ou ne peut pas lever d’exception ‣ avantage majeur : le compilateur a plus d’opportunités d’optimisations 120
  167. 167. noexcept 121 void foo() noexcept { // shall not throw any exception } struct foo { void operator()() const noexcept { // shall not throw any exception } }; sur fonctions libres ou membres
  168. 168. noexcept 122 opportunités d’optimisation auto vec = std::vector<foo>{}; const auto f = foo{}; vec.push_back(f); • Si vec doit agrandir son stockage interne ‣ C++03 : recopie - si une exception est lancée pendant la copie, vec original n’est pas modifié (strong guarantee) ‣ C++11 : strong guarantee à tenir - move si foo(foo&&) noexcept - copie sinon
  169. 169. noexcept 123 noexcept conditionnel (1) void bar() noexcept { // shall not throw any exception } void foo() noexcept(noexcept(bar())) { bar(); }
  170. 170. noexcept 124 noexcept conditionnel (2) struct bar { void operator()(int) noexcept {} }; struct baz { void operator()(int){} }; template <typename Fun> void foo(int x) noexcept(noexcept(Fun{}(x))) { Fun{}(x); } foo<bar>(33); foo<baz>(42);
  171. 171. noexcept 125 noexcept appliqué aux lambdas const auto lambda = [](int x) noexcept {return x + 40;}; std::cout << lambda(2);
  172. 172. • Destructeurs, operator delete et operator delete[] : noexcept par défaut • C’est une affirmation forte ‣ déclarer une fonction noexcept, puis plus tard enlever cette spécification peut casser le code client • Important pour ‣ foo(foo&&) ‣ foo& operator=(foo&&) ‣ swap(foo&, foo&) ‣ fonctions de calcul (sans allocations dynamiques)? ‣ …? 126 noexcept
  173. 173. constexpr 127 • const : spécifier des interfaces - foo(const std::vector<int>&); • constexpr : spécifier ce qui est utilisable dans des expressions constantes (et donc possiblement évaluées à la compilation) - tout ce qui est marqué constexpr doit pouvoir produire un résultat constant • Applicable aux objets et aux fonctions Visual Studio 2013
  174. 174. constexpr 128 auto sz = 3; // ERROR: sz's value not known at compilation constexpr auto sz1 = sz; // ERROR: sz's value not known at compilation auto array1 = std::array<int, sz>{}; // OK: 10 is a compile-time constant constexpr auto sz2 = 10; // OK: sz2 is constexpr auto array2 = std::array<int, sz2>{}; objets (1)
  175. 175. constexpr 129 const auto sz = 3; // OK: sz's value is known at compilation constexpr auto sz1 = sz; // OK: sz's value is known at compilation auto array1 = std::array<int, sz>{}; auto x = 3; const auto sz = x; // ERROR: sz's value not known at compilation constexpr auto sz1 = sz; // ERROR: sz's value not known at compilation auto array1 = std::array<int, sz>{}; objets (2)
  176. 176. constexpr 130 fonctions constexpr int foo(int a) { return a + 1; } constexpr auto x = foo(42); auto runtime_value = 0u; std::cin >> runtime_value; auto y = foo(runtime_value); fonction expression constante expression non-constante constexpr ✓ ✓ non-constexpr ✕ ✓
  177. 177. constexpr 131 C++11 seulement une expression autorisée constexpr int fac(int x) { return x <= 1 ? 1 : x * fac(x-1); } constexpr int fac(int x) { auto res = 1; for (auto i = 1; i <= x; ++i) { res *= i; } return res; } C++14 plusieurs instructions autorisées
  178. 178. constexpr 132 classes struct foo { int data; constexpr foo(int d) : data(d) {} constexpr int operator()() const {return data + 1;} constexpr operator int() const {return data;} }; auto f0 = foo{42}; constexpr auto f1 = foo{33}; auto i0 = f0(); constexpr auto i1 = f1(); constexpr auto i2 = static_cast<int>(f1); auto i3 = static_cast<int>(f1);
  179. 179. constexpr 132 classes struct foo { int data; constexpr foo(int d) : data(d) {} constexpr int operator()() const {return data + 1;} constexpr operator int() const {return data;} }; auto f0 = foo{42}; constexpr auto f1 = foo{33}; auto i0 = f0(); constexpr auto i1 = f1(); constexpr auto i2 = static_cast<int>(f1); auto i3 = static_cast<int>(f1); implicite en C++11 à mettre en C++14
  180. 180. constexpr 133 unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } constexpr unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } en pratique (clang)…
  181. 181. constexpr 133 unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } constexpr unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } pushq %rbp movq %rsp, %rbp movl $0x78, %eax popq %rbp retq en pratique (clang)…
  182. 182. constexpr 133 unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } constexpr unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } pushq %rbp movq %rsp, %rbp movl $0x78, %eax popq %rbp retq movdqa %xmm0, %xmm1 movhlps %xmm1, %xmm1 pshufd $0x31, %xmm0, %xmm2 pmuludq %xmm1, %xmm0 pshufd $0x31, %xmm1, %xmm1 pmuludq %xmm2, %xmm1 shufps $-0x78, %xmm1, %xmm0 pshufd $-0x28, %xmm0, %xmm0 pshufd $0x1, %xmm0, %xmm1 pshufd $0x31, %xmm0, %xmm2 pmuludq %xmm0, %xmm2 pmuludq %xmm0, %xmm1 shufps $-0x78, %xmm2, %xmm1 pshufd $-0x28, %xmm1, %xmm0 movd %xmm0, %eax cmpl %edx, %r8d je 0x100000f5d nopw %cs:(%rax,%rax) imull %edi, %eax leal -0x1(%rdi), %ecx cmpl $0x1, %ecx movl %ecx, %edi ja 0x100000f50 popq %rbp retq nop pushq %rbp movq %rsp, %rbp movl $0x78, %eax popq %rbp retq en pratique (clang)…
  183. 183. constexpr 133 unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } constexpr unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } pushq %rbp movq %rsp, %rbp movl $0x78, %eax popq %rbp retq pushq %rbp movq %rsp, %rbp movl $0x1, %eax cmpl $0x2, %edi jb 0x100000f5d leal -0x1(%rdi), %r8d movl %r8d, %ecx en pratique (clang)…
  184. 184. constexpr 133 unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } constexpr unsigned int f(unsigned int n) { return n <= 1 ? 1 : n * f(n-1); } int main() { return f(5); } Évaluation à l’exécution Évaluation à la compilation en pratique (clang)…
  185. 185. static_assert 134 #include <type_traits> template <typename T> constexpr bool is_power_of_two(T x) { static_assert( std::is_integral<T>::value and std::is_unsigned<T>::value , "Integral and unsigned type expected"); return x and ((x & (x-1)) == 0); } int main() { is_power_of_two(-2); } faire échouer la compilation sur un message clair error: static_assert failed "Integral and unsigned type expected"   static_assert( std::is_integral<T>::value and std::is_unsigned<T>::value   ^              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  186. 186. static_assert 134 #include <type_traits> template <typename T> constexpr bool is_power_of_two(T x) { static_assert( std::is_integral<T>::value and std::is_unsigned<T>::value , "Integral and unsigned type expected"); return x and ((x & (x-1)) == 0); } int main() { is_power_of_two(-2); } faire échouer la compilation sur un message clair error: static_assert failed "Integral and unsigned type expected"   static_assert( std::is_integral<T>::value and std::is_unsigned<T>::value   ^              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression constante requise
  187. 187. TEMPLATES
  188. 188. Généricité 136 struct foo_vector { foo* data; unsigned int sz; void push_back(const foo& x) { data[sz++] = x; } }; une nécessité pour les structures de données… auto int_vec = int_vector{}; auto foo_vec = foo_vector{}; int_vec.push_back(1); foo_vec.push_back(foo{}); struct int_vector { int* data; unsigned int sz; void push_back(const int& x) { data[sz++] = x; } };
  189. 189. Généricité 137 struct int_vector { int* data; unsigned int sz; void push_back(const int& x) { data[sz++] = x; } }; une nécessité pour les structures de données… struct foo_vector { foo* data; unsigned int sz; void push_back(const foo& x) { data[sz++] = x; } };
  190. 190. Généricité 138 template <typename T> struct vector { T* data; unsigned int sz; void push_back(const T& x) { data[sz++] = x; } }; une nécessité pour les structures de données… auto int_vec = vector<int>{}; auto foo_vec = vector<foo>{}; int_vec.push_back(1); foo_vec.push_back(foo{});
  191. 191. Généricité 138 template <typename T> struct vector { T* data; unsigned int sz; void push_back(const T& x) { data[sz++] = x; } }; une nécessité pour les structures de données… auto int_vec = vector<int>{}; auto foo_vec = vector<foo>{}; int_vec.push_back(1); foo_vec.push_back(foo{}); “instanciation” du type générique vector avec un entier
  192. 192. Généricité 139 int sum(const std::vector<int>& vec) { auto acc = int{}; for (const auto& x : vec) acc = acc + x; return acc; } une nécessité pour les algorithmes… const auto vec0 = std::vector<int>{1,2,3}; const auto s0 = sum(vec0); const auto vec1 = std::vector<foo>{1,2,3}; const auto s1 = sum(vec1); struct foo; foo operator+(const foo&, const foo&); foo sum(const std::vector<foo>& vec) { auto acc = foo{}; for (const auto& x : vec) acc = acc + x; return acc; }
  193. 193. Généricité 140 int sum(const std::vector<int>& vec) { auto acc = int{}; for (const auto& x : vec) acc = acc + x; return acc; } une nécessité pour les algorithmes… foo sum(const std::vector<foo>& vec) { auto acc = foo{}; for (const auto& x : vec) acc = acc + x; return acc; }
  194. 194. Généricité 141 template <typename T> T sum(const std::vector<T>& vec) { auto acc = T{}; for (const auto& x : vec) { acc = acc + x; } return acc; } une nécessité pour les algorithmes… const auto vec0 = std::vector<int>{1,2,3}; const auto s0 = sum(vec0); const auto vec1 = std::vector<foo>{1,2,3}; const auto s1 = sum(vec1);
  195. 195. Généricité 141 template <typename T> T sum(const std::vector<T>& vec) { auto acc = T{}; for (const auto& x : vec) { acc = acc + x; } return acc; } rien n’a changé! une nécessité pour les algorithmes… const auto vec0 = std::vector<int>{1,2,3}; const auto s0 = sum(vec0); const auto vec1 = std::vector<foo>{1,2,3}; const auto s1 = sum(vec1);
  196. 196. Structures templates 142 template <typename T> struct foo { T x_; foo(const T& x) : x_(x) {} const T& x() const {return x_;} T& x() {return x_;} }; auto f_int = foo<int>{42}; f_int.x() += 2; auto f_char = foo<char>{'a'}; f_char.x() = 'b';
  197. 197. Structures templates 142 template <typename T> struct foo { T x_; foo(const T& x) : x_(x) {} const T& x() const {return x_;} T& x() {return x_;} }; paramètre générique : c’est un type, pas une valeur auto f_int = foo<int>{42}; f_int.x() += 2; auto f_char = foo<char>{'a'}; f_char.x() = 'b';
  198. 198. Structures templates 142 template <typename T> struct foo { T x_; foo(const T& x) : x_(x) {} const T& x() const {return x_;} T& x() {return x_;} }; paramètre générique : c’est un type, pas une valeur accès au type dans toute la classe auto f_int = foo<int>{42}; f_int.x() += 2; auto f_char = foo<char>{'a'}; f_char.x() = 'b';
  199. 199. Fonctions templates 143 struct foo{}; template <typename T, typename U> void fun0(const T&, const U&); template <typename T, typename U> void fun1(const T&); fun0(0, foo{}); fun1<int, foo>(0); déduction automatique à partir des arguments
  200. 200. Fonctions templates 143 struct foo{}; template <typename T, typename U> void fun0(const T&, const U&); template <typename T, typename U> void fun1(const T&); fun0(0, foo{}); fun1<int, foo>(0); déduction automatique à partir des arguments tous les types sont présents dans les arguments
  201. 201. Fonctions templates 143 struct foo{}; template <typename T, typename U> void fun0(const T&, const U&); template <typename T, typename U> void fun1(const T&); fun0(0, foo{}); fun1<int, foo>(0); déduction automatique à partir des arguments tous les types sont présents dans les arguments les types ne sont pas tous présents dans les arguments, il donc les expliciter
  202. 202. Fonctions templates 144 template <typename T> struct foo { T x; }; template <typename T> foo<T> mk_foo(const T& x) { return {x}; } const auto f0 = foo<int>(42); const auto f1 = mk_foo(42); pratique pour “cacher” l’argument du template
  203. 203. Fonctions templates 144 template <typename T> struct foo { T x; }; template <typename T> foo<T> mk_foo(const T& x) { return {x}; } const auto f0 = foo<int>(42); const auto f1 = mk_foo(42); pratique pour “cacher” l’argument du template pas de type explicite
  204. 204. Fonctions templates 145 les fonctions membres peuvent être aussi génériques template <typename T> struct foo { T x_; foo(const T& x) : x_(x) {} template <typename U> void bar(const U&) {} }; auto f = foo<int>{42}; f.bar(3.22);
  205. 205. Fonctions templates 145 les fonctions membres peuvent être aussi génériques template <typename T> struct foo { T x_; foo(const T& x) : x_(x) {} template <typename U> void bar(const U&) {} }; auto f = foo<int>{42}; f.bar(3.22);méthode générique
  206. 206. Templates :“fabriques” 146 • Une structure template est une “fabrique de type” ‣ template <typename T> struct foo n’est pas un type - on ne peut pas créer d’objet de type foo<T> ‣ foo<int> est “généré” à partir de foo<T> - c’est un type, on peut créer un objet à partir de foo<int> • Une fonction template est une “fabrique de fonctions” ‣ on n’appelle pas directement template <typename T> foo()
  207. 207. Accès à un type imbriqué 147 struct bar { typedef int value_type; }; void foo(const bar&) { bar::value_type x; } dans un contexte non-générique
  208. 208. Accès à un type imbriqué 147 struct bar { typedef int value_type; }; void foo(const bar&) { bar::value_type x; } dans un contexte non-générique OK
  209. 209. Accès à un type imbriqué 148 struct bar { typedef int value_type; }; template <typename T> void foo(const T&) { T::value_type x; } error: missing 'typename' prior to dependent type name 'T::value_type'   T::value_type x;   ^~~~~~~~~~~~~   typename dans un contexte générique
  210. 210. Accès à un type imbriqué 148 struct bar { typedef int value_type; }; template <typename T> void foo(const T&) { T::value_type x; } error: missing 'typename' prior to dependent type name 'T::value_type'   T::value_type x;   ^~~~~~~~~~~~~   typename ! dans un contexte générique
  211. 211. Accès à un type imbriqué 149 struct bar { typedef int value_type; }; template <typename T> void foo(const T&) { typename T::value_type x; } dans un contexte générique
  212. 212. Accès à un type imbriqué 149 struct bar { typedef int value_type; }; template <typename T> void foo(const T&) { typename T::value_type x; } dans un contexte générique OK
  213. 213. 150 Dependant templates struct bar { template <typename U> U get() const {} }; template <typename T> void foo(const T& b) { const auto x = b.get<int>(); } auto b = bar{}; foo(b); error: use 'template' keyword to treat 'get' as a dependent template name   const auto x = b.get<T>();                    ^                    template
  214. 214. 150 Dependant templates struct bar { template <typename U> U get() const {} }; template <typename T> void foo(const T& b) { const auto x = b.get<int>(); } auto b = bar{}; foo(b); error: use 'template' keyword to treat 'get' as a dependent template name   const auto x = b.get<T>();                    ^                    template !
  215. 215. 151 Dependant templates struct bar { template <typename U> U get() const {} }; template <typename T> void foo(const T& b) { const auto x = b.template get<int>(); }
  216. 216. 151 Dependant templates struct bar { template <typename U> U get() const {} }; template <typename T> void foo(const T& b) { const auto x = b.template get<int>(); } OK
  217. 217. 151 Dependant templates struct bar { template <typename U> U get() const {} }; template <typename T> void foo(const T& b) { const auto x = b.template get<int>(); } OK get est une fonction template qui dépend du type T qui est lui même un paramètre template
  218. 218. alias 152 // the old way typedef int my_typedef; // aliases using my_alias = int; using : successeur de typedef
  219. 219. alias 152 // the old way typedef int my_typedef; // aliases using my_alias = int; lecture homogène avec auto var = …; using : successeur de typedef
  220. 220. alias 153 template <typename T, typename U> struct foo{}; template <typename T> struct templated_typedef { typedef foo<T, int> type; }; int main() { const auto f = templated_typedef<int>::type{}; } essayons tout de même (1) impossible d’avoir des typedef template
  221. 221. alias 154 template <typename T, typename U> struct foo{}; template <typename T> struct templated_typedef { typedef foo<T, int> type; }; template <typename T> void bar(const typename templated_typedef<T>::type&); int main() { const auto f = templated_typedef<int>::type{}; bar(f); } impossible d’avoir des typedef template essayons tout de même (2)
  222. 222. alias 154 template <typename T, typename U> struct foo{}; template <typename T> struct templated_typedef { typedef foo<T, int> type; }; template <typename T> void bar(const typename templated_typedef<T>::type&); int main() { const auto f = templated_typedef<int>::type{}; bar(f); } note: candidate template ignored: couldn't infer template argument 'T' void bar(const typename templated_typedef<T>::type&); impossible d’avoir des typedef template essayons tout de même (2) !
  223. 223. alias 155 template <typename T, typename U> struct foo{}; template <typename T> using alias_t = foo<T, int>; int main() { const auto f0 = foo<double, int>{}; const auto f1 = alias_t<int>{}; } alias templates
  224. 224. Règles de déductions 156 template <typename T> void f(ParamType param); f(expression); deux types à déduire template <typename T> void f(const T& param); int x = 42; f(x); pseudo-code :
  225. 225. Règles de déductions 156 template <typename T> void f(ParamType param); f(expression); type deT deux types à déduire template <typename T> void f(const T& param); int x = 42; f(x); pseudo-code :
  226. 226. Règles de déductions 156 template <typename T> void f(ParamType param); f(expression); type deT deux types à déduire type de param template <typename T> void f(const T& param); int x = 42; f(x); pseudo-code :
  227. 227. Règles de déductions 156 template <typename T> void f(ParamType param); f(expression); type deT deux types à déduire type de param template <typename T> void f(const T& param); int x = 42; f(x); int pseudo-code :
  228. 228. Règles de déductions 156 template <typename T> void f(ParamType param); f(expression); type deT deux types à déduire type de param template <typename T> void f(const T& param); int x = 42; f(x); int const int& pseudo-code :
  229. 229. Règles de déductions • Trois cas, le type de param ‣ est un pointeur ou une référence ‣ n’est ni un pointeur ni une référence ‣ est une référence universelle 157 template <typename T> void f(ParamType param);
  230. 230. Règles de déductions 158 ParamType : pointeur ou référence (1) template <typename T> void f(T& param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int int& f(cx) const int const int& f(rx) const int const int&
  231. 231. Règles de déductions 158 ParamType : pointeur ou référence (1) template <typename T> void f(T& param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int int& f(cx) const int const int& f(rx) const int const int& idem pour les pointeurs
  232. 232. Règles de déductions 159 ParamType : pointeur ou référence (2) template <typename T> void f(const T& param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int const int& f(cx) const int const int& f(rx) const int const int&
  233. 233. Règles de déductions 159 ParamType : pointeur ou référence (2) template <typename T> void f(const T& param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int const int& f(cx) const int const int& f(rx) const int const int& idem pour les pointeurs
  234. 234. Règles de déductions 160 ParamType : ni pointeur, ni référence template <typename T> void f(T param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int int f(cx) int int f(rx) int int
  235. 235. Règles de déductions 160 ParamType : ni pointeur, ni référence template <typename T> void f(T param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int int f(cx) int int f(rx) int int par valeur
  236. 236. Règles de déductions 160 ParamType : ni pointeur, ni référence template <typename T> void f(T param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int int f(cx) int int f(rx) int int par valeur copie : const est ignoré
  237. 237. Règles de déductions 161 ParamType : rvalue reference template <typename T> void f(T&& param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int& int& f(cx) const int& const int& f(rx) const int& const int& f(42) int int&& lvalues rvalues
  238. 238. Règles de déductions 161 ParamType : rvalue reference template <typename T> void f(T&& param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int& int& f(cx) const int& const int& f(rx) const int& const int& f(42) int int&& idem pour les pointeurs lvalues rvalues
  239. 239. Règles de déductions 161 ParamType : rvalue reference template <typename T> void f(T&& param); int x = 2; const int cx = x; const int& rx = x; T param f(x) int& int& f(cx) const int& const int& f(rx) const int& const int& f(42) int int&& idem pour les pointeurs lvalues rvalues “référence universelle”
  240. 240. 162 template <typename T> void print_type() { std::cout << __PRETTY_FUNCTION__; } Afficher le type déduit void print_type() [T = foo] (GCC, clang)
  241. 241. 163 template <typename T> void print_type() { std::cout << __FUNCSIG__; } void _cdecl print_type<struct foo>(void) (Visual Studio 2013) Afficher le type déduit
  242. 242. 164 template <typename T> struct print_type; print_type<foo>{}; (tous compilateurs) Afficher le type déduit error: implicit instantiation of undefined template 'print_type<foo>'   print_type<foo>{};   ^
  243. 243. 164 template <typename T> struct print_type; print_type<foo>{}; (tous compilateurs) Afficher le type déduit ne pas donner de définition error: implicit instantiation of undefined template 'print_type<foo>'   print_type<foo>{};   ^
  244. 244. Spécialisation 165 scénario : un algorithme peut-être optimisé pour un type particulier struct foo{}; template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <> struct algo<foo> { void operator()() {std::cout << "O(1)";} }; algo<foo>{}(); // “O(1)” algo<int>{}(); // “O(n)”
  245. 245. Spécialisation 165 scénario : un algorithme peut-être optimisé pour un type particulier struct foo{}; template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <> struct algo<foo> { void operator()() {std::cout << "O(1)";} }; algo<foo>{}(); // “O(1)” algo<int>{}(); // “O(n)” générique
  246. 246. Spécialisation 165 scénario : un algorithme peut-être optimisé pour un type particulier struct foo{}; template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <> struct algo<foo> { void operator()() {std::cout << "O(1)";} }; algo<foo>{}(); // “O(1)” algo<int>{}(); // “O(n)” spécialisation générique
  247. 247. Spécialisation 166 fonctions struct foo{}; template <typename T> void algo(const T&) { std::cout << "O(n)"; } template <> void algo(const foo&) { std::cout << "O(1)"; } algo(foo{}); // “O(1)” algo(42); // “O(n)”
  248. 248. Spécialisation 166 fonctions struct foo{}; template <typename T> void algo(const T&) { std::cout << "O(n)"; } template <> void algo(const foo&) { std::cout << "O(1)"; } algo(foo{}); // “O(1)” algo(42); // “O(n)” générique
  249. 249. Spécialisation 166 fonctions struct foo{}; template <typename T> void algo(const T&) { std::cout << "O(n)"; } template <> void algo(const foo&) { std::cout << "O(1)"; } algo(foo{}); // “O(1)” algo(42); // “O(n)” spécialisation générique
  250. 250. Spécialisation 167 fonctions, une petite surprise… template <typename T> void algo(const T&) { std::cout << "O(n)"; } template <> void algo(const foo&) { std::cout << "O(1)"; } void algo(const foo&) { std::cout << "O(1) bis"; } algo(foo{}); // “O(1) bis” algo(42); // “O(n)”
  251. 251. Spécialisation 167 fonctions, une petite surprise… template <typename T> void algo(const T&) { std::cout << "O(n)"; } template <> void algo(const foo&) { std::cout << "O(1)"; } void algo(const foo&) { std::cout << "O(1) bis"; } algo(foo{}); // “O(1) bis” algo(42); // “O(n)” spécialisation générique surcharge
  252. 252. Spécialisation 167 fonctions, une petite surprise… template <typename T> void algo(const T&) { std::cout << "O(n)"; } template <> void algo(const foo&) { std::cout << "O(1)"; } void algo(const foo&) { std::cout << "O(1) bis"; } algo(foo{}); // “O(1) bis” algo(42); // “O(n)” spécialisation générique surcharge préférer les surcharges aux spécialisations
  253. 253. Spécialisation partielle 168 scénario : un algorithme peut-être optimisé pour un conteneur générique particulier template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <typename T> struct algo<std::vector<T>> { void operator()() {std::cout << "O(1)";} }; algo<int>{}(); // “O(n)” algo<std::vector<int>>{}(); // “O(1)”
  254. 254. Spécialisation partielle 168 scénario : un algorithme peut-être optimisé pour un conteneur générique particulier template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <typename T> struct algo<std::vector<T>> { void operator()() {std::cout << "O(1)";} }; algo<int>{}(); // “O(n)” algo<std::vector<int>>{}(); // “O(1)” générique
  255. 255. Spécialisation partielle 168 scénario : un algorithme peut-être optimisé pour un conteneur générique particulier template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <typename T> struct algo<std::vector<T>> { void operator()() {std::cout << "O(1)";} }; algo<int>{}(); // “O(n)” algo<std::vector<int>>{}(); // “O(1)” générique spécialisation partielle
  256. 256. Spécialisation partielle 169 on peut garder des spécialisations totales template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <typename T> struct algo<std::vector<T>> { void operator()() {std::cout << "O(1)";} }; template <> struct algo<std::vector<double>> { void operator()() {std::cout << "O(1) double";} }; algo<int>{}(); // “O(n)” algo<std::vector<int>>{}(); // “O(1)” algo<std::vector<double>>{}(); // “O(1) double”
  257. 257. Spécialisation partielle 169 on peut garder des spécialisations totales template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <typename T> struct algo<std::vector<T>> { void operator()() {std::cout << "O(1)";} }; template <> struct algo<std::vector<double>> { void operator()() {std::cout << "O(1) double";} }; algo<int>{}(); // “O(n)” algo<std::vector<int>>{}(); // “O(1)” algo<std::vector<double>>{}(); // “O(1) double” générique
  258. 258. Spécialisation partielle 169 on peut garder des spécialisations totales template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <typename T> struct algo<std::vector<T>> { void operator()() {std::cout << "O(1)";} }; template <> struct algo<std::vector<double>> { void operator()() {std::cout << "O(1) double";} }; algo<int>{}(); // “O(n)” algo<std::vector<int>>{}(); // “O(1)” algo<std::vector<double>>{}(); // “O(1) double” générique spécialisation partielle
  259. 259. Spécialisation partielle 169 on peut garder des spécialisations totales template <typename T> struct algo { void operator()() {std::cout << "O(n)";} }; template <typename T> struct algo<std::vector<T>> { void operator()() {std::cout << "O(1)";} }; template <> struct algo<std::vector<double>> { void operator()() {std::cout << "O(1) double";} }; algo<int>{}(); // “O(n)” algo<std::vector<int>>{}(); // “O(1)” algo<std::vector<double>>{}(); // “O(1) double” générique spécialisation partielle spécialisation totale
  260. 260. Template-template arguments 170 template <typename T> struct vector {}; template <typename T, template <typename DUMMY> class Container> struct foo { Container<T> cont_; }; foo<int, vector>{}; prendre en paramètre template un template (1)
  261. 261. Template-template arguments 170 template <typename T> struct vector {}; template <typename T, template <typename DUMMY> class Container> struct foo { Container<T> cont_; }; foo<int, vector>{}; obligatoire prendre en paramètre template un template (1)
  262. 262. Template-template arguments 170 template <typename T> struct vector {}; template <typename T, template <typename DUMMY> class Container> struct foo { Container<T> cont_; }; foo<int, vector>{}; obligatoire nom sans importance peut être omis prendre en paramètre template un template (1)
  263. 263. Template-template arguments 170 template <typename T> struct vector {}; template <typename T, template <typename DUMMY> class Container> struct foo { Container<T> cont_; }; foo<int, vector>{}; obligatoire nom sans importance peut être omis T est passé à Container prendre en paramètre template un template (1)
  264. 264. Template-template arguments 171 template <typename T, template <typename> class Container> struct foo { Container<T> cont_; }; foo<int, std::vector>{}; prendre en paramètre template un template (2) error: template template argument has different template parameters than its corresponding template template parameter   foo<int, std::vector>{};                 ^ note: too many template parameters in template template argument template <class _Tp, class _Allocator = allocator<_Tp> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ attention aux nombres de paramètres…
  265. 265. Template-template arguments 171 template <typename T, template <typename> class Container> struct foo { Container<T> cont_; }; foo<int, std::vector>{}; prendre en paramètre template un template (2) ! error: template template argument has different template parameters than its corresponding template template parameter   foo<int, std::vector>{};                 ^ note: too many template parameters in template template argument template <class _Tp, class _Allocator = allocator<_Tp> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ attention aux nombres de paramètres…
  266. 266. Valeurs en paramètres template 172 template <int Value> struct foo { void operator()() const { std::cout << Value; } }; foo<42>{}(); on peut utiliser des valeurs comme paramètres template template <bool Value> struct foo { void operator()() const { std::cout << Value; } }; foo<true>{}();
  267. 267. Valeurs en paramètres template 173 expressions constantes seulement template <int Value> struct foo; const auto i = 42; auto j = i; foo<i>{}(); // OK foo<j>{}(); // ERROR error: non-type template argument is not a constant expression   foo<j>{}(); // ERROR       ^ note: read of non-const variable 'j' is not allowed in a constant expression
  268. 268. Valeurs en paramètres template 173 expressions constantes seulement template <int Value> struct foo; const auto i = 42; auto j = i; foo<i>{}(); // OK foo<j>{}(); // ERROR ! error: non-type template argument is not a constant expression   foo<j>{}(); // ERROR       ^ note: read of non-const variable 'j' is not allowed in a constant expression
  269. 269. Valeurs en paramètres template 174 la spécialisation fonctionne aussi template <int Value> struct fact { static const auto value = Value * fact<Value - 1>::value; }; template <> struct fact<0> { static const auto value = 1; }; std::cout << fact<5>::value; // 120
  270. 270. Valeurs en paramètres template 174 la spécialisation fonctionne aussi template <int Value> struct fact { static const auto value = Value * fact<Value - 1>::value; }; template <> struct fact<0> { static const auto value = 1; }; std::cout << fact<5>::value; // 120 récursion : cas général récursion : cas de base
  271. 271. Curiously RecurringTemplate Pattern (CRTP) 175 template <typename T> struct foo {}; struct bar : public foo<bar> {}; classe héritée générique
  272. 272. Variadic templates 176 template<typename T> T add(T x) { return x; } template<typename T, typename... Ts> T add(T x, Ts... xs) { return x + add(xs...); } add(1,2); // 3 add(1,2,3); // 6 add(1,2,3,4); // 10 // etc. nombre variable de paramètres template
  273. 273. Variadic templates 176 template<typename T> T add(T x) { return x; } template<typename T, typename... Ts> T add(T x, Ts... xs) { return x + add(xs...); } add(1,2); // 3 add(1,2,3); // 6 add(1,2,3,4); // 10 // etc. nombre variable de paramètres template “parameter pack”
  274. 274. Variadic templates 176 template<typename T> T add(T x) { return x; } template<typename T, typename... Ts> T add(T x, Ts... xs) { return x + add(xs...); } add(1,2); // 3 add(1,2,3); // 6 add(1,2,3,4); // 10 // etc. nombre variable de paramètres template expansion du pack “parameter pack”
  275. 275. Variadic templates 177 int add(int arg1) { return arg1; } int add(int arg1, int arg2) { return arg1 + add(arg2); } int add(int arg1, int arg2, int arg3) { return arg1 + add(arg2, arg3); } add(1,2,3); // 6 code “déroulé” template<typename T> T add(T x) { return x; } template<typename T, typename... Ts> T add(T x, Ts... xs) { return x + add(xs...); }
  276. 276. Variadic templates 178 raisonnement programmation fonctionnelle pure template<typename T> T add(T x) { return v; } template<typename T, typename... Ts> T add(T x, Ts... xs) { return x + add(xs...); } “déconstruction" de la liste de types head::tail Caml, Standard ML, Haskell, Scala, etc.
  277. 277. Variadic templates 178 raisonnement programmation fonctionnelle pure template<typename T> T add(T x) { return v; } template<typename T, typename... Ts> T add(T x, Ts... xs) { return x + add(xs...); } head “déconstruction" de la liste de types head::tail Caml, Standard ML, Haskell, Scala, etc.
  278. 278. Variadic templates 178 raisonnement programmation fonctionnelle pure template<typename T> T add(T x) { return v; } template<typename T, typename... Ts> T add(T x, Ts... xs) { return x + add(xs...); } head tail “déconstruction" de la liste de types head::tail Caml, Standard ML, Haskell, Scala, etc.
  279. 279. Variadic templates 178 raisonnement programmation fonctionnelle pure template<typename T> T add(T x) { return v; } template<typename T, typename... Ts> T add(T x, Ts... xs) { return x + add(xs...); } head tail “déconstruction" de la liste de types head::tail Caml, Standard ML, Haskell, Scala, etc. head
  280. 280. Variadic templates 179 template<typename T> T add(T v) { return v; } template<typename T, typename... Args> T add(T first, Args... args) { return first + add(args...); } std::string operator ""_s(const char *str, std::size_t) { return {str}; } add(“1”_s,"2"_s,"3"_s); // “123” pour le plaisir…
  281. 281. Variadic templates 180 // Signature of a meta-function that add integers template <int... Xs> struct add; template <int X, int... Xs> struct add<X, Xs...> { static const auto value = X + add<Xs...>::value; }; template <int X> struct add<X> { static const auto value = X ; }; add<1,2,3>::value; // 6 les struct/class aussi
  282. 282. Variadic templates 181 connaître la taille d’un pack avec sizeof... template <typename... Ts> std::size_t foo() { return sizeof...(Ts); } std::cout << foo<int, double, char>(); // 3
  283. 283. Variadic templates 182 expansion d’un pack (1) template <typename... Ts> struct foo{}; template<typename X, typename Y, typename... Z> void bar(X, Y, Z...) { print_type< foo<X, Y, Z...> >(); print_type< foo<X, Z..., Y> >(); print_type< foo<Z..., X, Y> >(); } bar(int{}, int{}, double{}, std::string{}); void print_type() [T = foo<int, int, double, std::__1::basic_string<char> >] void print_type() [T = foo<int, double, std::__1::basic_string<char>, int>] void print_type() [T = foo<double, std::__1::basic_string<char>, int, int>]
  284. 284. Variadic templates 182 expansion d’un pack (1) template <typename... Ts> struct foo{}; template<typename X, typename Y, typename... Z> void bar(X, Y, Z...) { print_type< foo<X, Y, Z...> >(); print_type< foo<X, Z..., Y> >(); print_type< foo<Z..., X, Y> >(); } bar(int{}, int{}, double{}, std::string{}); void print_type() [T = foo<int, int, double, std::__1::basic_string<char> >] void print_type() [T = foo<int, double, std::__1::basic_string<char>, int>] void print_type() [T = foo<double, std::__1::basic_string<char>, int, int>] Z… en dernière position
  285. 285. Variadic templates 183 expansion d’un pack (2) template<typename... Values> std::array<int, sizeof...(Values)> make_array(Values... values) { return std::array<int, sizeof...(Values)>{values...}; } const auto a = make_array(1,2,3); template<typename... Args> void foo(Args... values) { int tab[sizeof...(Args)] = {values...}; } foo(1,2,3);
  286. 286. Variadic templates 184 expansion d’un pack : héritage struct base1 { void foo(){} }; struct base2 { void bar(){} }; template <typename... Bases> class derived : public Bases... {}; auto d = derived<base1, base2>{}; d.foo(); d.bar();
  287. 287. Variadic templates 185 spécialisation template <typename... Ts> struct foo {}; template <typename... Ts> struct bar; template <typename... Ts> struct bar<foo<Ts...>> {}; using foo_type = foo<int, char>; using bar_type = bar<foo_type>;
  288. 288. Variadic templates 185 spécialisation template <typename... Ts> struct foo {}; template <typename... Ts> struct bar; template <typename... Ts> struct bar<foo<Ts...>> {}; using foo_type = foo<int, char>; using bar_type = bar<foo_type>; permet d’accéder à liste de paramètres de foo
  289. 289. Variadic templates 186 template <typename... Ts> struct tuple {}; template <typename T, typename... Ts> struct tuple<T, Ts...> : tuple<Ts...> { tuple(T t, Ts... ts) : tuple<Ts...>(ts...) , current(t) {} T current; }; structure récursive
  290. 290. Références universelles 187 template <typename T> void f(T&& param); • Dénomination de Scott Meyers • En présence d’une déduction de type (templates) • Il faudrait parler de ‣ forwarding reference ‣ en cours de standardisation (la dénomination) ‣ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/ n4164.pdf
  291. 291. Références universelles 187 template <typename T> void f(T&& param); • Dénomination de Scott Meyers • En présence d’une déduction de type (templates) • Il faudrait parler de ‣ forwarding reference ‣ en cours de standardisation (la dénomination) ‣ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/ n4164.pdf && sur paramètre template
  292. 292. Perfect forwarding 188 le problème : passer des paramètres de manière transparente à une sous-fonction template <typename Fun, typename T> void logger(const T& x) { std::cout << "beforen"; Fun{}(x); std::cout << "aftern"; } template <typename Fun, typename T> void logger(T& x) { std::cout << "beforen"; Fun{}(x); std::cout << "aftern"; } et s’il y a plusieurs paramètres?…
  293. 293. Perfect forwarding solution : std::forward template <typename Fun, typename Arg> void logger(Arg&& x) { std::cout << "beforen"; Fun{}(std::forward<Arg>(x)); std::cout << "aftern"; }
  294. 294. Perfect forwarding 190 comment ça fonctionne? (1) A& & → A& A& && → A& A&& & → A& A&& && → A&& si invocation f(a) a de type A Arg x lvalue A& A& rvalue A&& A&& template<typename Arg> void f(Arg&& x); “collapsing rules” application des collapsing rules
  295. 295. Perfect forwarding 191 comment ça fonctionne? (2) template<class T> T&& forward(typename remove_reference<T>::type& a) { return static_cast<T&&>(a); }
  296. 296. Perfect forwarding 192 void logger(X& && x) { std::cout << "beforen"; Fun{}(std::forward<X&>(x)); std::cout << "aftern"; } X& && forward(typename remove_reference<X&>::type& a) { return static_cast<X& &&>(a); } void logger(X& x) { std::cout << "beforen"; Fun{}(std::forward<X&>(x)); std::cout << "aftern"; } X& forward(X& a) { return static_cast<X&>(a); } sur une lvalue
  297. 297. Perfect forwarding 193 sur une rvalue void logger(X&& && x) { std::cout << "beforen"; Fun{}(std::forward<X&&>(x)); std::cout << "aftern"; } X&& && forward(typename remove_reference<X&&>::type& a) { return static_cast<X&& &&>(a); } void logger(X&& x) { std::cout << "beforen"; Fun{}(std::forward<X&&>(x)); std::cout << "aftern"; } X&& forward(X& a) { return static_cast<X&&>(a); }
  298. 298. Perfect forwarding vs move 194 void foo(int&&) {std::cout << "int&&";} void foo(const int&) {std::cout << "const int&";} void foo(int&) {std::cout << "int&";} template <typename T> void apply(T&& x) { std::cout << "A"; foo(std::forward<T>(x));} } void apply(int&& x) { std::cout << "B"; foo(std::move(x)); } apply(i); apply(ri); apply(cri); apply(42); apply(std::move(i)); quel est l’affichage? auto i = 2; auto& ri = i; const auto& cri = i;
  299. 299. BIBLIOTHÈQUE STANDARD
  300. 300. 196 begin/end auto vec = std::vector<int>{1,2,3}; auto it = begin(vec); // vec.begin() *it = 42; auto cit = cbegin(vec); // vec.cbegin() *cit = 33; // error fonctions libres for (auto cit = cbegin(vec); cit != cend(vec); ++cit) { // ... }
  301. 301. 197 begin/end tableaux C int tab[] = {1,2,3}; for (auto it = std::begin(tab); it != std::end(tab); ++it) { *it = 0; }
  302. 302. 197 begin/end tableaux C int tab[] = {1,2,3}; for (auto it = std::begin(tab); it != std::end(tab); ++it) { *it = 0; } appel “fully qualified”
  303. 303. 198 next/prev auto vec = std::vector<int>{1,2,3}; auto it = begin(vec); auto it1 = next(it); // return a copy incremented by 1 auto it2 = next(it, 2); // return a copy incremented by 2 auto it3 = prev(it2); // return a copy decremented by 1 auto it4 = prev(it3, 1); // return a copy decremented by 1
  304. 304. 199 move déplacer un conteneur auto vec1 = std::vector<foo>{foo{}, foo{}}; auto vec2 = std::vector<foo>{}; std::cout << vec1.size(); std::cout << vec2.size(); std::move(begin(vec1), end(vec1), std::back_inserter(vec2)); std::cout << vec1.size(); std::cout << vec2.size();
  305. 305. 200 all_of, any_of, any_of auto vec1 = std::vector<bool>{true, true, true}; auto vec2 = std::vector<bool>{true, false, true}; std::cout << std::boolalpha; std::cout << std::all_of( begin(vec1), end(vec1) , [](bool x){return x;}); std::cout << std::any_of( begin(vec2), end(vec2) , [](bool x){return not x;});
  306. 306. 201 emplace, emplace_back construction en place grâce au perfect forwarding struct foo { foo(int) {std::cout << "foo(int)";} foo(const foo&) {std::cout << "foo(const foo&)";} foo& operator=(const foo&) {std::cout << "operator=(const foo&)"; return *this;} foo(foo&&) {std::cout << "foo(foo&&)";} foo& operator=(foo&&) {std::cout << "operator=(foo&&)"; return *this;} }; auto vec1 = std::vector<foo>{}; vec1.reserve(3); auto f = foo{1}; vec1.push_back(f); vec1.push_back(foo{2}); vec1.emplace_back(3);
  307. 307. 202 shrink_to_fit deque, string et vector auto vec1 = std::vector<int>{}; std::cout << vec1.capacity(); vec1.push_back(1); vec1.push_back(1); vec1.push_back(1); std::cout << vec1.capacity(); vec1.shrink_to_fit(); std::cout << vec1.capacity(); peut libérer de la mémoire
  308. 308. 203 forward_list liste simplement chaînée auto l = std::forward_list<int>{1,2,3}; std::cout << l.size(); // error, no size() member auto cit0 = begin(l); auto cit1 = next(cit0); auto cit2 = prev(cit1); // error, not bidirectional
  309. 309. 204 array auto a1 = std::array<int, 3>{}; const auto cit = a1.cbegin(); const auto& x = a1[2]; // use like a C-array const auto& y = a1.at(10); // std::out_of_range auto a2 = std::array<int, 3>{}; if (a1 == a2) { // ... } auto a3 = a1; // etc. tableaux C pour le C++ strictement les mêmes performances! à privilégier dans tout nouveau code
  310. 310. 205 array const auto a0 = std::array<int, 3>{1,2,3}; const auto a1 = a0; toutes les facilités du C++ (1) const int t0[3] = {1,2,3}; int t1[3]; std::copy(t0, t0 + 3, t1); vs copie
  311. 311. 205 array const auto a0 = std::array<int, 3>{1,2,3}; const auto a1 = a0; toutes les facilités du C++ (1) dangereux const int t0[3] = {1,2,3}; int t1[3]; std::copy(t0, t0 + 3, t1); vs copie
  312. 312. 206 array const auto a0 = std::array<int, 3>{1,2,3}; const auto res = std::find(begin(a0), end(a0), 3); if (res != end(a0)) { std::cout << "foundn"; } toutes les facilités du C++ (2) utilisation des algorithmes STL
  313. 313. 207 array auto a0 = std::array<int, 3>{1, 2, 3}; auto a1 = std::array<int, 3>{1, 99, 3}; const auto res = memcmp(a0.data(), a1.data(), 3 * sizeof(int)); std::cout << res << 'n'; memcpy(a0.data(), a1.data(), 3 * sizeof(int)); for (auto x : a0) { std::cout << x << 'n'; } utilisation avec les fonctions C .data() pour accéder au tableau C
  314. 314. 208 tuple conteneur de types hétérogène auto t1 = std::tuple<int, char, foo>{2, 'a', foo{}}; auto t2 = std::make_tuple(2, 'a', foo{}); const auto& f = std::get<2>(t2);
  315. 315. 209 tuple permet de retourner plusieurs valeurs sans avoir à créer un nouveau type (1) struct foo {}; std::tuple<int, foo> bar() { return std::make_tuple(2, foo{}); } const auto t = bar();
  316. 316. 210 tuple permet de retourner plusieurs valeurs sans avoir à créer un nouveau type (2) struct foo {}; std::tuple<int, foo> bar() { return std::make_tuple(2, foo{}); } auto i = 0; auto f = foo{}; std::tie(i, f) = bar(); std::cout << i; // 2
  317. 317. 211 tuple concaténer des tuples auto t0 = std::make_tuple(1, foo{}, 'a'); std::cout << std::tuple_size<decltype(t0)>::value; // 3 auto t1 = std::make_tuple(3.14, std::string{"abc"}); std::cout << std::tuple_size<decltype(t1)>::value; // 2 auto t2 = std::tuple_cat(t0, t1); std::cout << std::tuple_size<decltype(t2)>::value; // 5
  318. 318. 212 unordered_map table de hachage auto map = std::unordered_map<int, std::string>{}; map.insert({42, "abc"}); map.emplace(33, "def"); for (const auto& key_value : map) { std::cout << key_value.first << " -> " << key_value.second; } fonctionne comme std::map
  319. 319. 213 unordered_map fonctions spécifiques aux tables de hachage auto map = std::unordered_map<int, std::string>{1'000'000}; peut-être initialisée avec un nombre de buckets map.rehash(2'000'000); peut-être redimensionnée map.load_factor() afficher le facteur de charge
  320. 320. 214 unordered_map utiliser un type personnalisé en tant que clé namespace std { template <> struct hash<foo> { std::size_t operator()(const foo& f) const { // hash function } }; } bool operator==(const foo&, const foo&); struct foo { // some data }; clé égalité hachage
  321. 321. 214 unordered_map utiliser un type personnalisé en tant que clé namespace std { template <> struct hash<foo> { std::size_t operator()(const foo& f) const { // hash function } }; } bool operator==(const foo&, const foo&); struct foo { // some data }; clé égalité hachage spécialisation
  322. 322. 215 unordered_map bonus: combiner des valeurs de hachage (1) /// @brief Combine the hash value of x with seed. /// /// Taken from <boost/functional/hash.hpp>. /// Sligthy modified to use std::hash<T> instead of /// boost::hash_value(). template <typename T> inline void hash_combine(std::size_t& seed, const T& x) noexcept(noexcept(std::hash<T>()(x))) { seed ^= std::hash<T>()(x) + 0x9e3779b9 + (seed<<6) + (seed>>2); }
  323. 323. 216 unordered_map bonus: combiner des valeurs de hachage (2) namespace std { template <> struct hash<foo> { std::size_t operator()(const foo& f) const { auto seed = 0ul; hash_combine(seed, f.x); hash_combine(seed, f.y); return seed; } }; }
  324. 324. 217 Gestion de la mémoire • introduction de unique_ptr ‣ auto_ptr deprecated ‣ utilise les portées pour désallouer • introduction de shared_ptr ‣ déjà présent dans Boost et POCCO ‣ comptage de référence • désallocation automatique dans tous les cas

×