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
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
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. 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
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)
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
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
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é
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
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. 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. 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
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
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;
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. 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. 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. 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. enum class
54
enum e {pink, white, orange, brown};
if (pink < 99.3)
{
// ...
}
enum C++03 : conversion implicite dangereuse
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. 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. 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. 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. 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. 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. 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
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
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. 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. 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. 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. 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
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Rvalues references
80
int foo() {return 2;}
int main()
{
foo() = 2;
}
error: lvalue required as left operand of assignment
foo() = 2;
^
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. 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. 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. 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.)
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. 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. 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. 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
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. 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
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. 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. 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. 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. 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. 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…
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. 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. 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
}
};
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 *]
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. 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. 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. 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. 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. 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. 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…
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. 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. 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. 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. 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. 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. 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. 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. 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)
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. 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. 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. 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. 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
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. 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. 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. 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
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. 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. 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. 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. 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. 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. 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. 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. 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. 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)…
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. 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. 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. 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
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. 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. 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. 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. 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. 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. 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. 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);
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. 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';
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
!
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. alias
152
// the old way
typedef int my_typedef;
// aliases
using my_alias = int;
using : successeur de typedef
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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”
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. 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. 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>{};
^
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. 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. 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. 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. 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. 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. 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. 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. 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. Curiously RecurringTemplate Pattern (CRTP)
175
template <typename T>
struct foo
{};
struct bar
: public foo<bar>
{};
classe héritée générique
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
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. 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. 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?…
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. 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. 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();
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. 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. 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. 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 dan