C++20 inclut les nouvelles fonctionnalités de langage suivantes :
C++20 inclut les nouvelles fonctionnalités de bibliothèque suivantes :
C++17 inclut les nouvelles fonctionnalités de langage suivantes :
C++17 inclut les nouvelles fonctionnalités de bibliothèque suivantes :
C++14 inclut les nouvelles fonctionnalités de langage suivantes :
C++14 inclut les nouvelles fonctionnalités de bibliothèque suivantes :
C++11 inclut les nouvelles fonctionnalités de langage suivantes :
C++11 inclut les nouvelles fonctionnalités de bibliothèque suivantes :
Remarque : Bien que ces exemples illustrent comment utiliser les coroutines à un niveau de base, il se passe beaucoup plus de choses lorsque le code est compilé. Ces exemples ne sont pas censés couvrir complètement les coroutines de C++20. Comme les classes
generator
ettask
ne sont pas encore fournies par la bibliothèque standard, j'ai utilisé la bibliothèque cppcoro pour compiler ces exemples.
Les coroutines sont des fonctions spéciales dont l'exécution peut être suspendue et reprise. Pour définir une coroutine, les mots-clés co_return
, co_await
ou co_yield
doivent être présents dans le corps de la fonction. Les coroutines de C++20 sont sans pile ; à moins qu'ils ne soient optimisés par le compilateur, leur état est alloué sur le tas.
Un exemple de coroutine est une fonction génératrice , qui renvoie (c'est-à-dire génère) une valeur à chaque invocation :
generator< int > range ( int start, int end) {
while (start < end) {
co_yield start;
start++;
}
// Implicit co_return at the end of this function:
// co_return;
}
for ( int n : range( 0 , 10 )) {
std::cout << n << std::endl;
}
La fonction de générateur range
ci-dessus génère des valeurs commençant au start
jusqu'à end
(exclusive), chaque étape d'itération produisant la valeur actuelle stockée dans start
. Le générateur conserve son état à chaque invocation de range
(dans ce cas, l'invocation s'effectue pour chaque itération de la boucle for). co_yield
prend l'expression donnée, renvoie (c'est-à-dire renvoie) sa valeur et suspend la coroutine à ce stade. À la reprise, l'exécution continue après le co_yield
.
Un autre exemple de coroutine est une tâche , qui est un calcul asynchrone exécuté lorsque la tâche est attendue :
task< void > echo (socket s) {
for (;;) {
auto data = co_await s. async_read ();
co_await async_write (s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
Dans cet exemple, le mot-clé co_await
est introduit. Ce mot clé prend une expression et suspend l'exécution si la chose que vous attendez (dans ce cas, la lecture ou l'écriture) n'est pas prête, sinon vous continuez l'exécution. (Notez que sous le capot, co_yield
utilise co_await
.)
Utiliser une tâche pour évaluer paresseusement une valeur :
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
Les concepts sont appelés prédicats à la compilation qui contraignent les types. Ils prennent la forme suivante :
template < template-parameter-list >
concept concept-name = constraint-expression;
où constraint-expression
est évaluée comme un booléen constexpr. Les contraintes doivent modéliser les exigences sémantiques, par exemple si un type est numérique ou hachable. Une erreur du compilateur se produit si un type donné ne satisfait pas le concept par lequel il est lié (c'est-à-dire que constraint-expression
renvoie false
). Étant donné que les contraintes sont évaluées au moment de la compilation, elles peuvent fournir des messages d'erreur plus significatifs et une sécurité d'exécution plus importante.
// `T` is not limited by any constraints.
template < typename T>
concept always_satisfied = true ;
// Limit `T` to integrals.
template < typename T>
concept integral = std::is_integral_v<T>;
// Limit `T` to both the `integral` constraint and signedness.
template < typename T>
concept signed_integral = integral<T> && std::is_signed_v<T>;
// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint.
template < typename T>
concept unsigned_integral = integral<T> && !signed_integral<T>;
Il existe une variété de formes syntaxiques pour appliquer les concepts :
// Forms for function parameters:
// `T` is a constrained type template parameter.
template <my_concept T>
void f (T v);
// `T` is a constrained type template parameter.
template < typename T>
requires my_concept<T>
void f (T v);
// `T` is a constrained type template parameter.
template < typename T>
void f (T v) requires my_concept<T>;
// `v` is a constrained deduced parameter.
void f (my_concept auto v);
// `v` is a constrained non-type template parameter.
template <my_concept auto v>
void g ();
// Forms for auto-deduced variables:
// `foo` is a constrained auto-deduced value.
my_concept auto foo = ...;
// Forms for lambdas:
// `T` is a constrained type template parameter.
auto f = []<my_concept T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []< typename T> requires my_concept<T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []< typename T> (T v) requires my_concept<T> {
// ...
};
// `v` is a constrained deduced parameter.
auto f = [](my_concept auto v) {
// ...
};
// `v` is a constrained non-type template parameter.
auto g = []<my_concept auto v> () {
// ...
};
Le mot-clé requires
est utilisé soit pour démarrer une clause requires
, soit une expression requires
:
template < typename T>
requires my_concept<T> // `requires` clause.
void f (T);
template < typename T>
concept callable = requires (T f) { f (); }; // `requires` expression.
template < typename T>
requires requires (T x) { x + x; } // `requires` clause and expression on same line.
T add (T a, T b) {
return a + b;
}
Notez que la liste des paramètres dans une expression requires
est facultative. Chaque exigence d'une expression requires
est l'une des suivantes :
template < typename T>
concept callable = requires (T f) { f (); };
typename
suivi d'un nom de type, affirme que le nom de type donné est valide. struct foo {
int foo;
};
struct bar {
using value = int ;
value data;
};
struct baz {
using value = int ;
value data;
};
// Using SFINAE, enable if `T` is a `baz`.
template < typename T, typename = std:: enable_if_t <std::is_same_v<T, baz>>>
struct S {};
template < typename T>
using Ref = T&;
template < typename T>
concept C = requires {
// Requirements on type `T`:
typename T::value; // A) has an inner member named `value`
typename S<T>; // B) must have a valid class template specialization for `S`
typename Ref<T>; // C) must be a valid alias template substitution
};
template <C T>
void g (T a);
g (foo{}); // ERROR: Fails requirement A.
g (bar{}); // ERROR: Fails requirement B.
g (baz{}); // PASS.
template < typename T>
concept C = requires(T x) {
{*x} -> std::convertible_to< typename T::inner>; // the type of the expression `*x` is convertible to `T::inner`
{x + 1 } -> std::same_as< int >; // the expression `x + 1` satisfies `std::same_as<decltype((x + 1))>`
{x * 1 } -> std::convertible_to<T>; // the type of the expression `x * 1` is convertible to `T`
};
requires
, spécifient des contraintes supplémentaires (telles que celles sur les arguments des paramètres locaux). template < typename T>
concept C = requires(T x) {
requires std::same_as< sizeof (x), size_t >;
};
Voir aussi : bibliothèque de concepts.
C++20 introduit l'opérateur de vaisseau spatial ( <=>
) comme une nouvelle façon d'écrire des fonctions de comparaison qui réduisent le passe-partout et aident les développeurs à définir une sémantique de comparaison plus claire. La définition d'un opérateur de comparaison à trois voies générera automatiquement les autres fonctions de l'opérateur de comparaison (c'est-à-dire ==
, !=
, <
, etc.).
Trois ordres sont introduits :
std::strong_ordering
: L'ordre fort fait la distinction entre les éléments égaux (identiques et interchangeables). Fournit un ordre less
, greater
, equivalent
et equal
. Exemples de comparaisons : recherche d'une valeur spécifique dans une liste, valeurs d'entiers, chaînes sensibles à la casse.std::weak_ordering
: L'ordre faible fait la distinction entre les éléments équivalents (non identiques, mais peuvent être interchangeables à des fins de comparaison). Fournit un ordre less
, greater
et equivalent
. Exemples de comparaisons : chaînes insensibles à la casse, tri, comparaison de certains membres visibles d'une classe, mais pas de tous.std::partial_ordering
: Le classement partiel suit le même principe de classement faible mais inclut le cas où un classement n'est pas possible. Fournit un ordre less
, greater
, equivalent
et unordered
. Exemples de comparaisons : valeurs à virgule flottante (par exemple NaN
).Un opérateur de comparaison à trois voies par défaut effectue une comparaison par membre :
struct foo {
int a;
bool b;
char c;
// Compare `a` first, then `b`, then `c` ...
auto operator <=>( const foo&) const = default ;
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == true
f1 == f2; // == false
f1 >= f2; // == false
Vous pouvez également définir vos propres comparaisons :
struct foo {
int x;
bool b;
char c;
std::strong_ordering operator <=>( const foo& other) const {
return x <=> other. x ;
}
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == false
f1 == f2; // == true
f1 >= f2; // == true
Syntaxe d'initialisation désignée de style C. Tous les champs membres qui ne sont pas explicitement répertoriés dans la liste d’initialisation désignée sont initialisés par défaut.
struct A {
int x;
int y;
int z = 123 ;
};
A a {. x = 1 , . z = 2 }; // a.x == 1, a.y == 0, a.z == 2
Utilisez la syntaxe de modèle familière dans les expressions lambda.
auto f = []< typename T>(std::vector<T> v) {
// ...
};
Cette fonctionnalité simplifie les modèles de code courants, permet de maintenir des portées strictes et offre une solution élégante à un problème courant de vie.
for ( auto v = std::vector{ 1 , 2 , 3 }; auto & e : v) {
std::cout << e;
}
// prints "123"
Fournit une indication à l’optimiseur indiquant que l’instruction étiquetée a une forte probabilité d’être exécutée.
switch (n) {
case 1 :
// ...
break ;
[[likely]] case 2 : // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break ;
}
Si l'un des attributs probables/improbables apparaît après la parenthèse droite d'une instruction if, cela indique qu'il est probable/peu probable que sa sous-instruction (corps) soit exécutée.
int random = get_random_number_between_x_and_y( 0 , 3 );
if (random > 0 ) [[likely]] {
// body of if statement
// ...
}
Il peut également être appliqué à la sous-instruction (corps) d’une instruction itérative.
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
La capture implicite this
dans une capture lambda à l'aide de [=]
est désormais obsolète ; préférez capturer explicitement en utilisant [=, this]
ou [=, *this]
.
struct int_value {
int n = 0 ;
auto getter_fn () {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, * this ]() { return n; };
}
};
Les classes peuvent désormais être utilisées dans des paramètres de modèle non-type. Les objets transmis comme arguments de modèle ont le type const T
, où T
est le type de l'objet et ont une durée de stockage statique.
struct foo {
foo () = default ;
constexpr foo ( int ) {}
};
template <foo f = {}>
auto get_foo () {
return f;
}
get_foo (); // uses implicit constructor
get_foo<foo{ 123 }>();
Les fonctions virtuelles peuvent désormais être constexpr
et évaluées au moment de la compilation. Les fonctions virtuelles constexpr
peuvent remplacer les fonctions virtuelles non constexpr
et vice-versa.
struct X1 {
virtual int f () const = 0;
};
struct X2 : public X1 {
constexpr virtual int f () const { return 2 ; }
};
struct X3 : public X2 {
virtual int f () const { return 3 ; }
};
struct X4 : public X3 {
constexpr virtual int f () const { return 4 ; }
};
constexpr X4 x4;
x4.f(); // == 4
Sélectionnez conditionnellement au moment de la compilation si un constructeur est rendu explicite ou non. explicit(true)
revient à spécifier explicit
.
struct foo {
// Specify non-integral types (strings, floats, etc.) require explicit construction.
template < typename T>
explicit (!std::is_integral_v<T>) foo(T) {}
};
foo a = 123 ; // OK
foo b = " 123 " ; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)
foo c { " 123 " }; // OK
Semblable aux fonctions constexpr
, mais les fonctions avec un spécificateur consteval
doivent produire une constante. C'est ce qu'on appelle immediate functions
.
consteval int sqr ( int n) {
return n * n;
}
constexpr int r = sqr( 100 ); // OK
int x = 100 ;
int r2 = sqr(x); // ERROR: the value of 'x' is not usable in a constant expression
// OK if `sqr` were a `constexpr` function
Intégrez les membres d’une énumération dans la portée pour améliorer la lisibilité. Avant:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel channel) {
switch (channel) {
case rgba_color_channel::red: return " red " ;
case rgba_color_channel::green: return " green " ;
case rgba_color_channel::blue: return " blue " ;
case rgba_color_channel::alpha: return " alpha " ;
}
}
Après:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel my_channel) {
switch (my_channel) {
using enum rgba_color_channel;
case red: return " red " ;
case green: return " green " ;
case blue: return " blue " ;
case alpha: return " alpha " ;
}
}
Capturez les packs de paramètres par valeur :
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
Capturez les packs de paramètres par référence :
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
Fournit un type standard pour représenter les chaînes UTF-8.
char8_t utf8_str[] = u8" u0123 " ;
Le spécificateur constinit
nécessite qu'une variable soit initialisée au moment de la compilation.
const char * g () { return " dynamic initialization " ; }
constexpr const char * f ( bool p) { return p ? " constant initializer " : g (); }
constinit const char * c = f( true ); // OK
constinit const char * d = g( false ); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time.
Aide à prendre en charge les macros variadiques en évaluant l'argument donné si la macro variadique n'est pas vide.
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
Des concepts sont également fournis par la bibliothèque standard pour créer des concepts plus complexes. Certains d'entre eux incluent :
Concepts linguistiques de base :
same_as
- spécifie que deux types sont identiques.derived_from
- spécifie qu'un type est dérivé d'un autre type.convertible_to
- spécifie qu'un type est implicitement convertible en un autre type.common_with
- spécifie que deux types partagent un type commun.integral
- spécifie qu'un type est un type intégral.default_constructible
- spécifie qu'un objet d'un type peut être construit par défaut.Notions de comparaison :
boolean
- spécifie qu'un type peut être utilisé dans des contextes booléens.equality_comparable
- spécifie que operator==
est une relation d'équivalence.Notions d'objet :
movable
- spécifie qu'un objet d'un type peut être déplacé et échangé.copyable
- spécifie qu'un objet d'un type peut être copié, déplacé et échangé.semiregular
- spécifie qu'un objet d'un type peut être copié, déplacé, échangé et construit par défaut.regular
- spécifie qu'un type est Regular , c'est-à-dire qu'il est à la fois semiregular
et equality_comparable
.Concepts appelables :
invocable
- spécifie qu'un type appelable peut être invoqué avec un ensemble donné de types d'arguments.predicate
- spécifie qu'un type appelable est un prédicat booléen.Voir aussi : notions.
Combinez la simplicité de printf
avec la sécurité de type de iostream
. Utilise des accolades comme espaces réservés et prend en charge un formatage personnalisé similaire aux spécificateurs de style printf.
std::format ( " {1} {0} " , " world " , " hello " ); // == "hello world"
int x = 123 ;
std::string str = std::format( " x: {} " , x); // str == "x: 123"
// Format to an output iterator:
for ( auto x : { 1 , 2 , 3 }) {
std::format_to (std::ostream_iterator< char >{std::cout, " n " }, " {} " , x);
}
Pour formater des types personnalisés :
struct fraction {
int numerator;
int denominator;
};
template <>
struct std ::formatter<fraction>
{
constexpr auto parse (std::format_parse_context& ctx) {
return ctx. begin ();
}
auto format ( const fraction& f, std::format_context& ctx) const {
return std::format_to (ctx. out (), " {0:d}/{1:d} " , f. numerator , f. denominator );
}
};
fraction f{ 1 , 2 };
std::format ( " {} " , f); // == "1/2"
Tamponne les opérations de sortie pour le flux de sortie encapsulé assurant la synchronisation (c'est-à-dire pas d'entrelacement de la sortie).
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
Un span est une vue (c'est-à-dire non propriétaire) d'un conteneur fournissant un accès contrôlé par les limites à un groupe d'éléments contigus. Étant donné que les vues ne possèdent pas leurs éléments, elles sont peu coûteuses à construire et à copier. Une façon simplifiée de penser aux vues est qu'elles contiennent des références à leurs données. Au lieu de conserver un pointeur/itérateur et un champ de longueur, un span regroupe les deux dans un seul objet.
Les étendues peuvent être de taille dynamique ou fixe (appelées étendues ). Les travées de taille fixe bénéficient de la vérification des limites.
Span ne propage pas const donc pour construire un span en lecture seule, utilisez std::span<const T>
.
Exemple : utiliser un span de taille dynamique pour imprimer des entiers à partir de divers conteneurs.
void print_ints (std::span< const int > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_ints (std::vector{ 1 , 2 , 3 });
print_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 });
int a[ 10 ] = { 0 };
print_ints (a);
// etc.
Exemple : une étendue de taille statique ne pourra pas être compilée pour les conteneurs qui ne correspondent pas à l'étendue de l'étendue.
void print_three_ints (std::span< const int , 3 > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_three_ints (std::vector{ 1 , 2 , 3 }); // ERROR
print_three_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 }); // ERROR
int a[ 10 ] = { 0 };
print_three_ints (a); // ERROR
std::array< int , 3 > b = { 1 , 2 , 3 };
print_three_ints (b); // OK
// You can construct a span manually if required:
std::vector c{ 1 , 2 , 3 };
print_three_ints (std::span< const int , 3 >{ c. data (), 3 }); // OK: set pointer and length field.
print_three_ints (std::span< const int , 3 >{ c. cbegin (), c. cend () }); // OK: use iterator pairs.
C++20 fournit un nouvel en-tête <bit>
qui fournit certaines opérations sur les bits, notamment popcount.
std::popcount ( 0u ); // 0
std::popcount ( 1u ); // 1
std::popcount ( 0b1111'0000u ); // 4
Constantes mathématiques incluant PI, nombre d'Euler, etc. définies dans l'en-tête <numbers>
.
std::numbers:: pi ; // 3.14159...
std::numbers::e; // 2.71828...
Fonction de prédicat qui est véridique lorsqu'elle est appelée dans un contexte de compilation.
constexpr bool is_compile_time () {
return std::is_constant_evaluated ();
}
constexpr bool a = is_compile_time(); // true
bool b = is_compile_time(); // false
auto p = std::make_shared< int []>( 5 ); // pointer to `int[5]`
// OR
auto p = std::make_shared< int [ 5 ]>(); // pointer to `int[5]`
Les chaînes (et les vues de chaînes) ont désormais les fonctions membres starts_with
et ends_with
pour vérifier si une chaîne commence ou se termine par la chaîne donnée.
std::string str = " foobar " ;
str.starts_with( " foo " ); // true
str.ends_with( " baz " ); // false
Les conteneurs associatifs tels que les ensembles et les cartes ont une fonction membre contains
, qui peut être utilisée à la place de l'idiome « rechercher et vérifier la fin de l'itérateur ».
std::map< int , char > map {{ 1 , ' a ' }, { 2 , ' b ' }};
map.contains( 2 ); // true
map.contains( 123 ); // false
std::set< int > set { 1 , 2 , 3 };
set.contains( 2 ); // true
Une manière plus sûre de réinterpréter un objet d’un type à un autre.
float f = 123.0 ;
int i = std::bit_cast< int >(f);
Calculez le milieu de deux entiers en toute sécurité (sans débordement).
std::midpoint ( 1 , 3 ); // == 2
Convertit l'objet array/"array-like" donné en un std::array
.
std::to_array ( " foo " ); // returns `std::array<char, 4>`
std::to_array< int >({ 1 , 2 , 3 }); // returns `std::array<int, 3>`
int a[] = { 1 , 2 , 3 };
std::to_array (a); // returns `std::array<int, 3>`
Lie les N premiers arguments (où N est le nombre d'arguments après la fonction donnée à std::bind_front
) à une fonction libre, lambda ou membre donnée.
const auto f = []( int a, int b, int c) { return a + b + c; };
const auto g = std::bind_front(f, 1 , 1 );
g ( 1 ); // == 3
Fournit std::erase
et/ou std::erase_if
pour une variété de conteneurs STL tels que chaîne, liste, vecteur, carte, etc.
Pour effacer par valeur, utilisez std::erase
, ou pour spécifier un prédicat quand effacer les éléments, utilisez std::erase_if
. Les deux fonctions renvoient le nombre d'éléments effacés.
std::vector v{ 0 , 1 , 0 , 2 , 0 , 3 };
std::erase (v, 0 ); // v == {1, 2, 3}
std::erase_if (v, []( int n) { return n == 0 ; }); // v == {1, 2, 3}
Fonctions d'assistance pour donner des noms aux résultats de comparaison :
std::is_eq ( 0 <=> 0 ); // == true
std::is_lteq ( 0 <=> 1 ); // == true
std::is_gt ( 0 <=> 1 ); // == false
Voir aussi : comparaison à trois.
Compare lexicographiquement deux plages à l'aide d'une comparaison à trois voies et produit un résultat du type de catégorie de comparaison applicable le plus fort.
std::vector a{ 0 , 0 , 0 }, b{ 0 , 0 , 0 }, c{ 1 , 1 , 1 };
auto cmp_ab = std::lexicographical_compare_three_way(
a.begin(), a.end(), b.begin(), b.end());
std::is_eq (cmp_ab); // == true
auto cmp_ac = std::lexicographical_compare_three_way(
a.begin(), a.end(), c.begin(), c.end());
std::is_lt (cmp_ac); // == true
Voir aussi : comparaison à trois voies, aides à la comparaison à trois voies.
Déduction automatique des arguments de modèle, un peu comme c'est le cas pour les fonctions, mais incluant désormais les constructeurs de classe.
template < typename T = float >
struct MyContainer {
T val;
MyContainer () : val{} {}
MyContainer (T val) : val{val} {}
// ...
};
MyContainer c1 { 1 }; // OK MyContainer<int>
MyContainer c2; // OK MyContainer<float>
En suivant les règles de déduction de auto
, tout en respectant la liste des paramètres de modèle non-type des types autorisés[*], les arguments du modèle peuvent être déduits des types de ses arguments :
template < auto ... seq>
struct my_integer_sequence {
// Implementation here ...
};
// Explicitly pass type `int` as template argument.
auto seq = std::integer_sequence< int , 0 , 1 , 2 >();
// Type is deduced to be `int`.
auto seq2 = my_integer_sequence< 0 , 1 , 2 >();
* - Par exemple, vous ne pouvez pas utiliser un double
comme type de paramètre de modèle, ce qui en fait également une déduction invalide en utilisant auto
.
Une expression de repli effectue le repli d'un pack de paramètres de modèle sur un opérateur binaire.
(... op e)
ou (e op ...)
, où op
est un opérateur de repli et e
est un pack de paramètres non développé, est appelée replis unaires .(e1 op ... op e2)
, où op
sont des opérateurs de repli, est appelée un repli binaire . Soit e1
, soit e2
est un pack de paramètres non développé, mais pas les deux. template < typename ... Args>
bool logicalAnd (Args... args) {
// Binary folding.
return ( true && ... && args);
}
bool b = true ;
bool & b2 = b;
logicalAnd (b, b2, true ); // == true
template < typename ... Args>
auto sum (Args... args) {
// Unary folding.
return (... + args);
}
sum ( 1.0 , 2 . 0f , 3 ); // == 6.0
Modifications apportées à la déduction auto
lorsqu'elle est utilisée avec la syntaxe d'initialisation uniforme. Auparavant, auto x {3};
en déduit un std::initializer_list<int>
, qui en déduit maintenant int
.
auto x1 { 1 , 2 , 3 }; // error: not a single element
auto x2 = { 1 , 2 , 3 }; // x2 is std::initializer_list<int>
auto x3 { 3 }; // x3 is int
auto x4 { 3.0 }; // x4 is double
Lambdas au moment de la compilation en utilisant constexpr
.
auto identity = []( int n) constexpr { return n; };
static_assert (identity( 123 ) == 123);
constexpr auto add = []( int x, int y) {
auto L = [=] { return x; };
auto R = [=] { return y; };
return [=] { return L () + R (); };
};
static_assert (add( 1 , 2 )() == 3);
constexpr int addOne ( int n) {
return [n] { return n + 1 ; }();
}
static_assert (addOne( 1 ) == 2);
this
par valeur Capturer this
dans un environnement lambda était auparavant uniquement une référence. Un exemple de cas où cela pose problème est le code asynchrone utilisant des rappels qui nécessitent qu'un objet soit disponible, potentiellement au-delà de sa durée de vie. *this
(C++17) fera désormais une copie de l'objet actuel, tandis que this
(C++11) continue de capturer par référence.
struct MyObj {
int value { 123 };
auto getValueCopy () {
return [* this ] { return value; };
}
auto getValueRef () {
return [ this ] { return value; };
}
};
MyObj mo;
auto valueCopy = mo.getValueCopy();
auto valueRef = mo.getValueRef();
mo.value = 321 ;
valueCopy (); // 123
valueRef (); // 321
Le spécificateur en ligne peut être appliqué aux variables ainsi qu'aux fonctions. Une variable déclarée en ligne a la même sémantique qu'une fonction déclarée en ligne.
// Disassembly example using compiler explorer.
struct S { int x; };
inline S x1 = S{ 321 }; // mov esi, dword ptr [x1]
// x1: .long 321
S x2 = S{ 123 }; // mov eax, dword ptr [.L_ZZ4mainE2x2]
// mov dword ptr [rbp - 8], eax
// .L_ZZ4mainE2x2: .long 123
Il peut également être utilisé pour déclarer et définir une variable membre statique, de sorte qu'elle n'ait pas besoin d'être initialisée dans le fichier source.
struct S {
S () : id{count++} {}
~S () { count--; }
int id;
static inline int count{ 0 }; // declare and initialize count to 0 within the class
};
Utilisation de l'opérateur de résolution d'espace de noms pour créer des définitions d'espace de noms imbriquées.
namespace A {
namespace B {
namespace C {
int i;
}
}
}
Le code ci-dessus peut s'écrire ainsi :
namespace A ::B::C {
int i;
}
Une proposition d'initialisation déstructurante, qui permettrait d'écrire auto [ x, y, z ] = expr;
où le type de expr
était un objet de type tuple, dont les éléments seraient liés aux variables x
, y
et z
(que déclare cette construction). Les objets de type tuple incluent std::tuple
, std::pair
, std::array
et les structures agrégées.
using Coordinate = std::pair< int , int >;
Coordinate origin () {
return Coordinate{ 0 , 0 };
}
const auto [ x, y ] = origin();
x; // == 0
y; // == 0
std::unordered_map<std::string, int > mapping {
{ " a " , 1 },
{ " b " , 2 },
{ " c " , 3 }
};
// Destructure by reference.
for ( const auto & [key, value] : mapping) {
// Do something with key and value
}
Nouvelles versions des instructions if
et switch
qui simplifient les modèles de code courants et aident les utilisateurs à garder des portées strictes.
{
std::lock_guard<std::mutex> lk (mx);
if (v. empty ()) v. push_back (val);
}
// vs.
if (std::lock_guard<std::mutex> lk (mx); v.empty()) {
v. push_back (val);
}
Foo gadget (args);
switch ( auto s = gadget.status()) {
case OK: gadget. zip (); break ;
case Bad: throw BadFoo (s. message ());
}
// vs.
switch (Foo gadget (args); auto s = gadget.status()) {
case OK: gadget. zip (); break ;
case Bad: throw BadFoo (s. message ());
}
Écrivez du code instancié en fonction d'une condition de compilation.
template < typename T>
constexpr bool isIntegral () {
if constexpr (std::is_integral<T>::value) {
return true ;
} else {
return false ;
}
}
static_assert (isIntegral< int >() == true);
static_assert (isIntegral< char >() == true);
static_assert (isIntegral< double >() == false);
struct S {};
static_assert (isIntegral<S>() == false);
Un littéral de caractère commençant par u8
est un littéral de caractère de type char
. La valeur d'un littéral de caractère UTF-8 est égale à sa valeur de point de code ISO 10646.
char x = u8 ' x ' ;
Les énumérations peuvent désormais être initialisées en utilisant une syntaxe entre accolades.
enum byte : unsigned char {};
byte b { 0 }; // OK
byte c {- 1 }; // ERROR
byte d = byte{ 1 }; // OK
byte e = byte{ 256 }; // ERROR
C++17 introduit trois nouveaux attributs : [[fallthrough]]
, [[nodiscard]]
et [[maybe_unused]]
.
[[fallthrough]]
indique au compilateur que l'échec d'une instruction switch est un comportement prévu. Cet attribut ne peut être utilisé que dans une instruction switch et doit être placé avant l'étiquette case/default suivante. switch (n) {
case 1 :
// ...
[[fallthrough]];
case 2 :
// ...
break ;
case 3 :
// ...
[[fallthrough]];
default :
// ...
}
[[nodiscard]]
émet un avertissement lorsqu'une fonction ou une classe possède cet attribut et que sa valeur de retour est ignorée. [[nodiscard]] bool do_something () {
return is_success; // true for success, false for failure
}
do_something (); // warning: ignoring return value of 'bool do_something()',
// declared with attribute 'nodiscard'
// Only issues a warning when `error_info` is returned by value.
struct [[nodiscard]] error_info {
// ...
};
error_info do_something () {
error_info ei;
// ...
return ei;
}
do_something (); // warning: ignoring returned value of type 'error_info',
// declared with attribute 'nodiscard'
[[maybe_unused]]
indique au compilateur qu'une variable ou un paramètre peut être inutilisé et est prévu. void my_callback (std::string msg, [[maybe_unused]] bool error) {
// Don't care if `msg` is an error message, just log it.
log (msg);
}
L'opérateur __has_include (operand)
peut être utilisé dans les expressions #if
et #elif
pour vérifier si un en-tête ou un fichier source ( operand
) est disponible pour l'inclusion ou non.
Un cas d'utilisation serait d'utiliser deux bibliothèques qui fonctionnent de la même manière, en utilisant celle de sauvegarde/expérimentale si la bibliothèque préférée n'est pas trouvée sur le système.
# ifdef __has_include
# if __has_include(<optional>)
# include < optional >
# define have_optional 1
# elif __has_include(<experimental/optional>)
# include < experimental/optional >
# define have_optional 1
# define experimental_optional
# else
# define have_optional 0
# endif
# endif
Il peut également être utilisé pour inclure des en-têtes existant sous différents noms ou emplacements sur différentes plates-formes, sans savoir sur quelle plate-forme le programme s'exécute, les en-têtes OpenGL en sont un bon exemple qui se trouvent dans le répertoire OpenGL
sur macOS et GL
sur d'autres plates-formes.
# ifdef __has_include
# if __has_include(<OpenGL/gl.h>)
# include < OpenGL/gl.h >
# include < OpenGL/glu.h >
# elif __has_include(<GL/gl.h>)
# include < GL/gl.h >
# include < GL/glu.h >
# else
# error No suitable OpenGL headers found.
# endif
# endif
La déduction d'arguments de modèle de classe (CTAD) permet au compilateur de déduire les arguments de modèle à partir des arguments du constructeur.
std::vector v{ 1 , 2 , 3 }; // deduces std::vector<int>
std::mutex mtx;
auto lck = std::lock_guard{ mtx }; // deduces to std::lock_guard<std::mutex>
auto p = new std::pair{ 1.0 , 2.0 }; // deduces to std::pair<double, double>*
Pour les types définis par l'utilisateur, des guides de déduction peuvent être utilisés pour guider le compilateur sur la manière de déduire les arguments du modèle, le cas échéant :
template < typename T>
struct container {
container (T t) {}
template < typename Iter>
container (Iter beg, Iter end);
};
// deduction guide
template < typename Iter>
container (Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
container a{ 7 }; // OK: deduces container<int>
std::vector< double > v{ 1.0 , 2.0 , 3.0 };
auto b = container{ v. begin (), v. end () }; // OK: deduces container<double>
container c{ 5 , 6 }; // ERROR: std::iterator_traits<int>::value_type is not a type
Le modèle de classe std::variant
représente une union
de type sécurisé. Une instance de std::variant
contient à un moment donné une valeur de l'un de ses types alternatifs (il est également possible qu'elle soit sans valeur).
std::variant< int , double > v{ 12 };
std::get< int >(v); // == 12
std::get< 0 >(v); // == 12
v = 12.0 ;
std::get< double >(v); // == 12.0
std::get< 1 >(v); // == 12.0
Le modèle de classe std::optional
gère une valeur contenue facultative, c'est à dire une valeur qui peut être présente ou non. Un cas d'utilisation courant de facultatif est la valeur de retour d'une fonction qui peut échouer.
std::optional<std::string> create ( bool b) {
if (b) {
return " Godzilla " ;
} else {
return {};
}
}
create ( false ).value_or( " empty " ); // == "empty"
create ( true ).value(); // == "Godzilla"
// optional-returning factory functions are usable as conditions of while and if
if ( auto str = create( true )) {
// ...
}
Un conteneur de type sécurisé pour les valeurs uniques de tout type.
std::any x { 5 };
x.has_value() // == true
std::any_cast< int >(x) // == 5
std::any_cast< int &>(x) = 10 ;
std::any_cast< int >(x) // == 10
Une référence non propriétaire à une chaîne. Utile pour fournir une abstraction au-dessus des chaînes (par exemple pour l'analyse).
// Regular strings.
std::string_view cppstr { " foo " };
// Wide strings.
std::wstring_view wcstr_v { L" baz " };
// Character arrays.
char array[ 3 ] = { ' b ' , ' a ' , ' r ' };
std::string_view array_v (array, std::size(array));
std::string str { " trim me " };
std::string_view v {str};
v.remove_prefix(std::min(v.find_first_not_of( " " ), v.size()));
str; // == " trim me"
v; // == "trim me"
Invoquez un objet Callable
avec des paramètres. Des exemples d'objets appelables sont std::function
ou lambdas ; objets qui peuvent être appelés de la même manière qu’une fonction régulière.
template < typename Callable>
class Proxy {
Callable c_;
public:
Proxy (Callable c) : c_{ std::move (c) } {}
template < typename ... Args>
decltype ( auto ) operator ()(Args&&... args) {
// ...
return std::invoke (c_, std::forward<Args>(args)...);
}
};
const auto add = []( int x, int y) { return x + y; };
Proxy p{ add };
p ( 1 , 2 ); // == 3
Invoquez un objet Callable
avec un tuple d'arguments.
auto add = []( int x, int y) {
return x + y;
};
std::apply (add, std::make_tuple( 1 , 2 )); // == 3
La nouvelle bibliothèque std::filesystem
fournit un moyen standard de manipuler les fichiers, répertoires et chemins d'accès dans un système de fichiers.
Ici, un gros fichier est copié dans un chemin temporaire s'il y a de l'espace disponible :
const auto bigFilePath { " bigFileToCopy " };
if (std::filesystem::exists(bigFilePath)) {
const auto bigFileSize { std::filesystem::file_size (bigFilePath)};
std::filesystem::path tmpPath { " /tmp " };
if ( std::filesystem::space (tmpPath). available > bigFileSize) {
std::filesystem::create_directory (tmpPath. append ( " example " ));
std::filesystem::copy_file (bigFilePath, tmpPath. append ( " newFile " ));
}
}
Le nouveau type std::byte
fournit une manière standard de représenter les données sous forme d'octet. Les avantages de l'utilisation std::byte
sur char
ou unsigned char
sont qu'il ne s'agit pas d'un type de caractère et qu'il ne s'agit pas non plus d'un type arithmétique ; tandis que les seules surcharges d'opérateurs disponibles sont les opérations au niveau du bit.
std::byte a { 0 };
std::byte b { 0xFF };
int i = std::to_integer< int >(b); // 0xFF
std::byte c = a & b;
int j = std::to_integer< int >(c); // 0
Notez que std::byte
est simplement une énumération et que l'initialisation entre accolades des énumérations devient possible grâce à l'initialisation directe par liste des énumérations.
Déplacement de nœuds et fusion de conteneurs sans les frais généraux liés aux copies, déplacements ou allocations/désallocations de tas coûteux.
Déplacer des éléments d'une carte à une autre :
std::map< int , string> src {{ 1 , " one " }, { 2 , " two " }, { 3 , " buckle my shoe " }};
std::map< int , string> dst {{ 3 , " three " }};
dst.insert(src.extract(src.find( 1 ))); // Cheap remove and insert of { 1, "one" } from `src` to `dst`.
dst.insert(src.extract( 2 )); // Cheap remove and insert of { 2, "two" } from `src` to `dst`.
// dst == { { 1, "one" }, { 2, "two" }, { 3, "three" } };
Insertion d'un ensemble entier :
std::set< int > src { 1 , 3 , 5 };
std::set< int > dst { 2 , 4 , 5 };
dst.merge(src);
// src == { 5 }
// dst == { 1, 2, 3, 4, 5 }
Insérer des éléments qui survivent au conteneur :
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
Changer la clé d'un élément de carte :
std::map< int , string> m {{ 1 , " one " }, { 2 , " two " }, { 3 , " three " }};
auto e = m.extract( 2 );
e.key() = 4 ;
m.insert(std::move(e));
// m == { { 1, "one" }, { 3, "three" }, { 4, "two" } }
De nombreux algorithmes STL, tels que les méthodes copy
, find
et sort
, ont commencé à prendre en charge les politiques d'exécution parallèle : seq
, par
et par_unseq
qui se traduisent par "séquentiellement", "parallèle" et "parallèle non séquencé".
std::vector< int > longVector;
// Find element using parallel execution policy
auto result1 = std::find(std::execution::par, std::begin(longVector), std::end(longVector), 2 );
// Sort elements using sequential execution policy
auto result2 = std::sort(std::execution::seq, std::begin(longVector), std::end(longVector));
Échantillonne n éléments dans la séquence donnée (sans remplacement) où chaque élément a une chance égale d'être sélectionné.
const std::string ALLOWED_CHARS = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " ;
std::string guid;
// Sample 5 characters from ALLOWED_CHARS.
std::sample (ALLOWED_CHARS.begin(), ALLOWED_CHARS.end(), std::back_inserter(guid),
5, std::mt19937{ std::random_device{}() });
std::cout << guid; // e.g. G1fW2
Fixer la valeur donnée entre une limite inférieure et supérieure.
std::clamp ( 42 , - 1 , 1 ); // == 1
std::clamp (- 42 , - 1 , 1 ); // == -1
std::clamp ( 0 , - 1 , 1 ); // == 0
// `std::clamp` also accepts a custom comparator:
std::clamp ( 0 , - 1 , 1 , std::less<>{}); // == 0
Repliez une gamme donnée d’éléments. Conceptuellement similaire à std::accumulate
, mais std::reduce
effectuera le pli en parallèle. Le pliage étant effectué en parallèle, si vous spécifiez une opération binaire, elle doit être associative et commutative. Une opération binaire donnée ne doit également modifier aucun élément ni invalider aucun itérateur dans la plage donnée.
L'opération binaire par défaut est std::plus avec une valeur initiale de 0.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::reduce (std::cbegin(a), std::cend(a)); // == 6
// Using a custom binary op:
std::reduce (std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6
De plus, vous pouvez spécifier des transformations pour les réducteurs :
std::transform_reduce (std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten); // == 60
const std::array< int , 3 > b{ 1 , 2 , 3 };
const auto product_times_ten = []( const auto a, const auto b) { return a * b * 10 ; };
std::transform_reduce (std::cbegin(a), std::cend(a), std::cbegin(b), 0, std::plus<>{}, product_times_ten); // == 140
Prise en charge des sommes de préfixes (analyses inclusives et exclusives) ainsi que des transformations.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}); // 1 3 6
std::exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}); // 0 1 3
const auto times_ten = []( const auto n) { return n * 10 ; };
std::transform_inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}, times_ten); // 10 30 60
std::transform_exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}, times_ten); // 0 10 30
Plus grand diviseur commun (PGCD) et plus petit commun multiple (LCM).
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
Fonction utilitaire qui renvoie la négation du résultat de la fonction donnée.
const std::ostream_iterator< int > ostream_it{ std::cout, " " };
const auto is_even = []( const auto n) { return n % 2 == 0 ; };
std::vector< int > v{ 0 , 1 , 2 , 3 , 4 };
// Print all even numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, is_even); // 0 2 4
// Print all odd (not even) numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, std::not_fn(is_even)); // 1 3
Convertissez les intégrales et les flottants en chaîne ou vice versa. Les conversions ne sont pas lancées, ne sont pas allouées et sont plus sécurisées que les équivalents de la bibliothèque standard C.
Les utilisateurs sont responsables d'allouer suffisamment de stockage requis pour std::to_chars
, sinon la fonction échouera en définissant l'objet code d'erreur dans sa valeur de retour.
Ces fonctions vous permettent éventuellement de transmettre une base (par défaut en base 10) ou un spécificateur de format pour une entrée de type flottant.
std::to_chars
renvoie un pointeur char (non const) qui se situe après la fin de la chaîne dans laquelle la fonction a écrit dans le tampon donné, et un objet code d'erreur.std::from_chars
renvoie un pointeur const char qui, en cas de succès, est égal au pointeur de fin transmis à la fonction, et un objet code d'erreur.Les deux objets de code d'erreur renvoyés par ces fonctions sont égaux à l'objet de code d'erreur initialisé par défaut en cas de succès.
Convertissez le nombre 123
en std::string
:
const int n = 123 ;
// Can use any container, string, array, etc.
std::string str;
str.resize( 3 ); // hold enough storage for each digit of `n`
const auto [ ptr, ec ] = std::to_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << str << std::endl; } // 123
else { /* handle failure */ }
Convertir d'un std::string
avec la valeur "123"
en un entier :
const std::string str{ " 123 " };
int n;
const auto [ ptr, ec ] = std::from_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << n << std::endl; } // 123
else { /* handle failure */ }
Fournit des fonctions d'assistance abs, round, ceil et floor pour std::chrono::duration
et std::chrono::time_point
.
using seconds = std::chrono::seconds;
std::chrono::milliseconds d{ 5500 };
std::chrono::abs (d); // == 5s
std::chrono::round<seconds>(d); // == 6s
std::chrono::ceil<seconds>(d); // == 6s
std::chrono::floor<seconds>(d); // == 5s
Les littéraux binaires constituent un moyen pratique de représenter un nombre en base 2. Il est possible de séparer les chiffres par '
.
0b110 // == 6
0b1111'1111 // == 255
C++14 autorise désormais le spécificateur de type auto
dans la liste des paramètres, permettant ainsi les lambdas polymorphes.
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
Cela permet de créer des captures lambda initialisées avec des expressions arbitraires. Le nom donné à la valeur capturée n'a pas besoin d'être lié à des variables dans les étendues englobantes et introduit un nouveau nom dans le corps lambda. L'expression d'initialisation est évaluée lorsque le lambda est créé (pas lorsqu'il est invoqué ).
int factory ( int i) { return i * 10 ; }
auto f = [x = factory( 2 )] { return x; }; // returns 20
auto generator = [x = 0 ] () mutable {
// this would not compile without 'mutable' as we are modifying x on each call
return x++;
};
auto a = generator(); // == 0
auto b = generator(); // == 1
auto c = generator(); // == 2
Parce qu'il est désormais possible de déplacer (ou d'avancer ) des valeurs dans un lambda qui ne pouvaient auparavant être capturées que par copie ou référence, nous pouvons désormais capturer des types de déplacement uniquement dans un lambda par valeur. Notez que dans l'exemple ci-dessous, le p
dans la liste de capture de task2
sur le côté gauche de =
est une nouvelle variable privée du corps lambda et ne fait pas référence au p
d'origine.
auto p = std::make_unique< int >( 1 );
auto task1 = [=] { *p = 5 ; }; // ERROR: std::unique_ptr cannot be copied
// vs.
auto task2 = [p = std::move(p)] { *p = 5 ; }; // OK: p is move-constructed into the closure object
// the original p is empty after task2 is created
L'utilisation de cette référence-captures peut avoir des noms différents de ceux de la variable référencée.
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
En utilisant un type de retour auto
en C++14, le compilateur tentera de déduire le type pour vous. Avec lambdas, vous pouvez désormais déduire son type de retour en utilisant auto
, ce qui permet de renvoyer une référence déduite ou une référence rvalue.
// Deduce return type as `int`.
auto f ( int i) {
return i;
}
template < typename T>
auto & f (T& t) {
return t;
}
// Returns a reference to a deduced type.
auto g = []( auto & x) -> auto & { return f (x); };
int y = 123 ;
int & z = g(y); // reference to `y`
Le spécificateur de type decltype(auto)
déduit également un type comme le fait auto
. Cependant, il en déduit les types de retour tout en conservant leurs références et qualificatifs de CV, contrairement à auto
.
const int x = 0 ;
auto x1 = x; // int
decltype ( auto ) x2 = x; // const int
int y = 0 ;
int & y1 = y;
auto y2 = y1; // int
decltype ( auto ) y3 = y1; // int&
int && z = 0 ;
auto z1 = std::move(z); // int
decltype ( auto ) z2 = std::move(z); // int&&
// Note: Especially useful for generic code!
// Return type is `int`.
auto f ( const int & i) {
return i;
}
// Return type is `const int&`.
decltype ( auto ) g( const int & i) {
return i;
}
int x = 123 ;
static_assert (std::is_same< const int &, decltype(f(x))>::value == 0);
static_assert (std::is_same< int , decltype(f(x))>::value == 1);
static_assert (std::is_same< const int &, decltype(g(x))>::value == 1);
Voir aussi : decltype (C++11)
.
En C++ 11, les corps de fonction constexpr
ne pouvaient contenir qu'un ensemble très limité de syntaxes, y compris (mais sans s'y limiter) : typedef
s, using
s et une seule instruction return
. En C++14, l'ensemble des syntaxes autorisées s'étend considérablement pour inclure la syntaxe la plus courante telle que les instructions if
, return
multiples, les boucles, etc.
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
C++14 permet de modéliser les variables :
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
C++14 introduit l'attribut [[deprecated]]
pour indiquer qu'une unité (fonction, classe, etc.) est déconseillée et génère probablement des avertissements de compilation. Si une raison est fournie, elle sera incluse dans les avertissements.
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
Nouveaux littéraux définis par l'utilisateur pour les types de bibliothèques standard, y compris de nouveaux littéraux intégrés pour chrono
et basic_string
. Ceux-ci peuvent être constexpr
, ce qui signifie qu'ils peuvent être utilisés au moment de la compilation. Certaines utilisations de ces littéraux incluent l'analyse d'entiers au moment de la compilation, les littéraux binaires et les littéraux de nombres imaginaires.
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
Le modèle de classe std::integer_sequence
représente une séquence d'entiers au moment de la compilation. Il y a quelques assistants construits au-dessus :
std::make_integer_sequence<T, N>
- crée une séquence de 0, ..., N - 1
avec le type T
.std::index_sequence_for<T...>
- convertit un pack de paramètres de modèle en une séquence entière.Convertissez un tableau en tuple :
template < typename Array, std:: size_t ... I>
decltype ( auto ) a2t_impl( const Array& a, std::integer_sequence<std:: size_t , I...>) {
return std::make_tuple (a[I]...);
}
template < typename T, std:: size_t N, typename Indices = std::make_index_sequence<N>>
decltype ( auto ) a2t( const std::array<T, N>& a) {
return a2t_impl (a, Indices ());
}
std::make_unique
est la méthode recommandée pour créer des instances de std::unique_ptr
s pour les raisons suivantes :
new
opérateur.foo
comme ceci : foo (std::unique_ptr<T>{ new T{}}, function_that_throws(), std::unique_ptr<T>{ new T{}});
Le compilateur est libre d'appeler new T{}
, puis function_that_throws()
, et ainsi de suite... Puisque nous avons alloué des données sur le tas lors de la première construction d'un T
, nous avons introduit ici une fuite. Avec std::make_unique
, nous obtenons une sécurité d'exception :
foo (std::make_unique<T>(), function_that_throws(), std::make_unique<T>());
Voir la section sur les pointeurs intelligents (C++11) pour plus d'informations sur std::unique_ptr
et std::shared_ptr
.
Déplacer un objet signifie transférer la propriété d’une ressource qu’il gère vers un autre objet.
Le premier avantage de la sémantique des déplacements est l’optimisation des performances. Lorsqu'un objet est sur le point d'atteindre la fin de sa durée de vie, soit parce qu'il s'agit d'un objet temporaire, soit en appelant explicitement std::move
, un déplacement est souvent un moyen moins coûteux de transférer des ressources. Par exemple, déplacer un std::vector
revient simplement à copier certains pointeurs et états internes vers le nouveau vecteur -- la copie impliquerait de devoir copier chaque élément contenu dans le vecteur, ce qui est coûteux et inutile si l'ancien vecteur sera bientôt détruit.
Les déplacements permettent également aux types non copiables tels que std::unique_ptr
s (pointeurs intelligents) de garantir au niveau du langage qu'il n'y a qu'une seule instance d'une ressource gérée à la fois, tout en pouvant transférer une instance entre les portées.
Voir les sections sur : les références rvalue, les fonctions membres spéciales pour la sémantique de déplacement, std::move
, std::forward
, forwarding references
.
C++11 introduit une nouvelle référence appelée référence rvalue . Une référence rvalue à T
, qui est un paramètre de type non-modèle (tel que int
ou un type défini par l'utilisateur), est créée avec la syntaxe T&&
. Les références Rvalue se lient uniquement aux rvalues.
Déduction de type avec lvalues et rvalues :
int x = 0 ; // `x` is an lvalue of type `int`
int & xl = x; // `xl` is an lvalue of type `int&`
int && xr = x; // compiler error -- `x` is an lvalue
int && xr2 = 0 ; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
void f ( int & x) {}
void f ( int && x) {}
f (x); // calls f(int&)
f (xl); // calls f(int&)
f ( 3 ); // calls f(int&&)
f (std::move(x)); // calls f(int&&)
f (xr2); // calls f(int&)
f (std::move(xr2)); // calls f(int&& x)
Voir aussi : std::move
, std::forward
, forwarding references
.
Également connu (officieusement) sous le nom de références universelles . Une référence de transfert est créée avec la syntaxe T&&
où T
est un paramètre de type de modèle, ou en utilisant auto&&
. Cela permet une transmission parfaite : la possibilité de transmettre des arguments tout en conservant leur catégorie de valeur (par exemple, les lvalues restent en tant que lvalues, les temporaires sont transmises en tant que rvalues).
Les références de transfert permettent à une référence de se lier à une lvalue ou à une rvalue selon le type. Les références de transfert suivent les règles de réduction des références :
T& &
devient T&
T& &&
devient T&
T&& &
devient T&
T&& &&
devient T&&
déduction de type auto
avec lvalues et rvalues :
int x = 0 ; // `x` is an lvalue of type `int`
auto && al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x`
auto && ar = 0 ; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
Déduction des paramètres de type modèle avec lvalues et rvalues :
// Since C++14 or later:
void f ( auto && t) {
// ...
}
// Since C++11 or later:
template < typename T>
void f (T&& t) {
// ...
}
int x = 0 ;
f ( 0 ); // T is int, deduces as f(int &&) => f(int&&)
f (x); // T is int&, deduces as f(int& &&) => f(int&)
int & y = x;
f (y); // T is int&, deduces as f(int& &&) => f(int&)
int && z = 0 ; // NOTE: `z` is an lvalue with type `int&&`.
f (z); // T is int&, deduces as f(int& &&) => f(int&)
f (std::move(z)); // T is int, deduces as f(int &&) => f(int&&)
Voir aussi : std::move
, std::forward
, rvalue references
.
La syntaxe ...
crée un pack de paramètres ou en développe un. Un pack de paramètres de modèle est un paramètre de modèle qui accepte zéro ou plusieurs arguments de modèle (non-types, types ou modèles). Un modèle avec au moins un pack de paramètres est appelé modèle variadique .
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
Une utilisation intéressante consiste à créer une liste d'initialiseurs à partir d'un pack de paramètres afin de parcourir les arguments de fonction variadiques.
template < typename First, typename ... Args>
auto sum ( const First first, const Args... args) -> decltype(first) {
const auto values = {first, args...};
return std::accumulate (values. begin (), values. end (), First{ 0 });
}
sum ( 1 , 2 , 3 , 4 , 5 ); // 15
sum ( 1 , 2 , 3 ); // 6
sum ( 1.5 , 2.0 , 3.7 ); // 7.2
Un conteneur léger de type tableau d'éléments créé à l'aide d'une syntaxe de « liste entre accolades ». Par exemple, { 1, 2, 3 }
crée une séquence d'entiers de type std::initializer_list<int>
. Utile en remplacement du passage d'un vecteur d'objets à une fonction.
int sum ( const std::initializer_list< int >& list) {
int total = 0 ;
for ( auto & e : list) {
total += e;
}
return total;
}
auto list = { 1 , 2 , 3 };
sum (list); // == 6
sum ({ 1 , 2 , 3 }); // == 6
sum ({}); // == 0
Assertions évaluées au moment de la compilation.
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
les variables auto
-typées sont déduites par le compilateur en fonction du type de leur initialiseur.
auto a = 3.14 ; // double
auto b = 1 ; // int
auto & c = b; // int&
auto d = { 0 }; // std::initializer_list<int>
auto && e = 1 ; // int&&
auto && f = b; // int&
auto g = new auto ( 123 ); // int*
const auto h = 1 ; // const int
auto i = 1 , j = 2 , k = 3 ; // int, int, int
auto l = 1 , m = true , n = 1.61 ; // error -- `l` deduced to be int, `m` is bool
auto o; // error -- `o` requires initializer
Extrêmement utile pour la lisibilité, notamment pour les types compliqués :
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
Les fonctions peuvent également déduire le type de retour en utilisant auto
. En C++11, un type de retour doit être spécifié soit explicitement, soit en utilisant decltype
comme ceci :
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2 ); // == 3
add ( 1 , 2.0 ); // == 3.0
add ( 1.5 , 1.5 ); // == 3.0
Le type de retour final dans l'exemple ci-dessus est le type déclaré (voir la section sur decltype
) de l'expression x + y
. Par exemple, si x
est un entier et y
est un double, decltype(x + y)
est un double. Par conséquent, la fonction ci-dessus déduira le type en fonction du type que produit l'expression x + y
. Notez que le type de retour final a accès à ses paramètres, et this
lorsque cela est approprié.
Un lambda
est un objet fonction sans nom capable de capturer des variables dans sa portée. Il comporte : une liste de capture ; un ensemble facultatif de paramètres avec un type de retour final facultatif ; et un corps. Exemples de listes de capture :
[]
- ne capture rien.[=]
- capture les objets locaux (variables locales, paramètres) dans la portée par valeur.[&]
- capture les objets locaux (variables locales, paramètres) dans la portée par référence.[this]
- capturez this
par référence.[a, &b]
- capture les objets a
par valeur, b
par référence. int x = 1 ;
auto getX = [=] { return x; };
getX (); // == 1
auto addX = [=]( int y) { return x + y; };
addX ( 1 ); // == 2
auto getXRef = [&]() -> int & { return x; };
getXRef (); // int& to `x`
Par défaut, les captures de valeurs ne peuvent pas être modifiées dans le lambda car la méthode générée par le compilateur est marquée comme const
. Le mot clé mutable
permet de modifier les variables capturées. Le mot-clé est placé après la liste de paramètres (qui doit être présente même si elle est vide).
int x = 1 ;
auto f1 = [&x] { x = 2 ; }; // OK: x is a reference and modifies the original
auto f2 = [x] { x = 2 ; }; // ERROR: the lambda can only perform const-operations on the captured value
// vs.
auto f3 = [x]() mutable { x = 2 ; }; // OK: the lambda can perform any operations on the captured value
decltype
est un opérateur qui renvoie le type déclaré d'une expression qui lui est transmise. Les qualificatifs cv et les références sont conservés s'ils font partie de l'expression. Exemples de decltype
:
int a = 1 ; // `a` is declared as type `int`
decltype (a) b = a; // `decltype(a)` is `int`
const int & c = a; // `c` is declared as type `const int&`
decltype (c) d = a; // `decltype(c)` is `const int&`
decltype ( 123 ) e = 123; // `decltype(123)` is `int`
int && f = 1 ; // `f` is declared as type `int&&`
decltype (f) g = 1; // `decltype(f) is `int&&`
decltype ((a)) h = g; // `decltype((a))` is int&
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2.0 ); // `decltype(x + y)` => `decltype(3.0)` => `double`
Voir aussi : decltype(auto) (C++14)
.
Sémantiquement similaires à l'utilisation d'un typedef
, cependant, les alias de type avec using
sont plus faciles à lire et sont compatibles avec les modèles.
template < typename T>
using Vec = std::vector<T>;
Vec< int > v; // std::vector<int>
using String = std::string;
String s { " foo " };
C++11 introduit un nouveau type de pointeur nul conçu pour remplacer la macro NULL
de C. nullptr
lui-même est de type std::nullptr_t
et peut être implicitement converti en types pointeur, et contrairement à NULL
, non convertible en types intégraux sauf bool
.
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
Énumérations de type sécurisé qui résolvent une variété de problèmes avec les énumérations de style C, notamment : les conversions implicites, l'incapacité de spécifier le type sous-jacent, la pollution de la portée.
// Specifying underlying type as `unsigned int`
enum class Color : unsigned int { Red = 0xff0000 , Green = 0xff00 , Blue = 0xff };
// `Red`/`Green` in `Alert` don't conflict with `Color`
enum class Alert : bool { Red, Green };
Color c = Color::Red;
Les attributs fournissent une syntaxe universelle sur __attribute__(...)
, __declspec
, etc.
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
Les expressions constantes sont des expressions qui sont peut-être évaluées par le compilateur au moment de la compilation. Seuls les calculs non complexes peuvent être effectués dans une expression constante (ces règles sont progressivement assouplies dans les versions ultérieures). Utilisez le spécificateur constexpr
pour indiquer la variable, la fonction, etc. est une expression constante.
constexpr int square ( int x) {
return x * x;
}
int square2 ( int x) {
return x * x;
}
int a = square( 2 ); // mov DWORD PTR [rbp-4], 4
int b = square2( 2 ); // mov edi, 2
// call square2(int)
// mov DWORD PTR [rbp-8], eax
Dans l'extrait précédent, notez que le calcul lors de l'appel square
est effectué au temps de compilation, puis le résultat est intégré dans la génération de code, tandis que square2
est appelé au moment de l'exécution.
Les valeurs constexpr
sont celles que le compilateur peut évaluer, mais ne sont pas garanties, au moment de la compilation:
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
Expressions constantes avec des cours:
struct Complex {
constexpr Complex ( double r, double i) : re{r}, im{i} { }
constexpr double real () { return re; }
constexpr double imag () { return im; }
private:
double re;
double im;
};
constexpr Complex I ( 0 , 1 );
Les constructeurs peuvent désormais appeler d'autres constructeurs dans la même classe à l'aide d'une liste d'initialisateur.
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
Les littéraux définis par l'utilisateur vous permettent d'étendre la langue et d'ajouter votre propre syntaxe. Pour créer un littéral, définissez un T operator "" X(...) { ... }
Fonction qui renvoie un type T
, avec un nom X
. Notez que le nom de cette fonction définit le nom du littéral. Tous les noms littéraux ne commençant pas par un soulignement sont réservés et ne seront pas invoqués. Il existe des règles sur les paramètres d'une fonction littérale définie par l'utilisateur, selon le type que le littéral est appelé.
Conversion Celsius en Fahrenheit:
// `unsigned long long` parameter required for integer literal.
long long operator " " _celsius( unsigned long long tempCelsius) {
return std::llround (tempCelsius * 1.8 + 32 );
}
24_celsius; // == 75
Conversion de chaîne à entière:
// `const char*` and `std::size_t` required as parameters.
int operator " " _int( const char * str, std:: size_t ) {
return std::stoi (str);
}
" 123 " _int; // == 123, with type `int`
Spécifie qu'une fonction virtuelle remplace une autre fonction virtuelle. Si la fonction virtuelle ne remplace pas la fonction virtuelle d'un parent, lance une erreur du compilateur.
struct A {
virtual void foo ();
void bar ();
};
struct B : A {
void foo () override ; // correct -- B::foo overrides A::foo
void bar () override ; // error -- A::bar is not virtual
void baz () override ; // error -- B::baz does not override A::baz
};
Spécifie qu'une fonction virtuelle ne peut pas être remplacée dans une classe dérivée ou qu'une classe ne peut pas être héritée.
struct A {
virtual void foo ();
};
struct B : A {
virtual void foo () final ;
};
struct C : B {
virtual void foo (); // error -- declaration of 'foo' overrides a 'final' function
};
La classe ne peut pas être héritée.
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
Un moyen plus élégant et efficace de fournir une implémentation par défaut d'une fonction, comme un constructeur.
struct A {
A () = default ;
A ( int x) : x{x} {}
int x { 1 };
};
A a; // a.x == 1
A a2 { 123 }; // a.x == 123
Avec héritage:
struct B {
B () : x{ 1 } {}
int x;
};
struct C : B {
// Calls B::B
C () = default ;
};
C c; // c.x == 1
Un moyen plus élégant et efficace de fournir une implémentation supprimée d'une fonction. Utile pour prévenir des copies sur les objets.
class A {
int x;
public:
A ( int x) : x{x} {};
A ( const A&) = delete ;
A& operator =( const A&) = delete ;
};
A x { 123 };
A y = x; // error -- call to deleted copy constructor
y = x; // error -- operator= deleted
Sucre syntaxique pour itérer sur les éléments d'un récipient.
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int & x : a) x *= 2 ;
// a == { 2, 4, 6, 8, 10 }
Remarquez la différence lors de l'utilisation int
par opposition à int&
:
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int x : a) x *= 2 ;
// a == { 1, 2, 3, 4, 5 }
Le constructeur de copie et l'opérateur d'affectation de copie sont appelés lorsque des copies sont fabriquées, et avec l'introduction par C ++ 11 de Move Sémantique, il y a maintenant un constructeur de déménagement et un opérateur d'attribution de déplacement pour les mouvements.
struct A {
std::string s;
A () : s{ " test " } {}
A ( const A& o) : s{o. s } {}
A (A&& o) : s{ std::move (o. s )} {}
A& operator =(A&& o) {
s = std::move (o. s );
return * this ;
}
};
A f (A a) {
return a;
}
A a1 = f(A{}); // move-constructed from rvalue temporary
A a2 = std::move(a1); // move-constructed using std::move
A a3 = A{};
a2 = std::move(a3); // move-assignment using std::move
a1 = f(A{}); // move-assignment from rvalue temporary
Les constructeurs de conversion convertiront les valeurs de la syntaxe de la liste contrevenue en arguments de constructeur.
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
};
A a { 0 , 0 }; // calls A::A(int, int)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(int, int)
A d { 0 , 0 , 0 }; // calls A::A(int, int, int)
Notez que la syntaxe de la liste des contreventes ne permet pas de rétrécir:
struct A {
A ( int ) {}
};
A a ( 1.1 ); // OK
A b { 1.1 }; // Error narrowing conversion from double to int
Notez que si un constructeur accepte un std::initializer_list
, il sera appelé à la place:
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
A (std::initializer_list< int >) {}
};
A a { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A d { 0 , 0 , 0 }; // calls A::A(std::initializer_list<int>)
Les fonctions de conversion peuvent désormais être rendues explicites à l'aide du spécificateur explicit
.
struct A {
operator bool () const { return true ; }
};
struct B {
explicit operator bool () const { return true ; }
};
A a;
if (a); // OK calls A::operator bool()
bool ba = a; // OK copy-initialization selects A::operator bool()
B b;
if (b); // OK calls B::operator bool()
bool bb = b; // error copy-initialization does not consider B::operator bool()
Tous les membres d'un espace de noms en ligne sont traités comme s'ils faisaient partie de son espace de noms parent, permettant la spécialisation des fonctions et l'assouplissement du processus de versioning. Il s'agit d'une propriété transitive, si A contient B, qui à son tour contient C et B et C sont des espaces de noms en ligne, les membres de C peuvent être utilisés comme s'ils étaient sur A.
namespace Program {
namespace Version1 {
int getVersion () { return 1 ; }
bool isFirstVersion () { return true ; }
}
inline namespace Version2 {
int getVersion () { return 2 ; }
}
}
int version { Program::getVersion ()}; // Uses getVersion() from Version2
int oldVersion { Program::Version1::getVersion ()}; // Uses getVersion() from Version1
bool firstVersion { Program::isFirstVersion ()}; // Does not compile when Version2 is added
Permet d'initialiser les membres de données non statiques lorsqu'ils sont déclarés, nettoyant potentiellement les constructeurs d'initialisations par défaut.
// Default initialization prior to C++11
class Human {
Human () : age{ 0 } {}
private:
unsigned age;
};
// Default initialization on C++11
class Human {
private:
unsigned age { 0 };
};
C ++ 11 est désormais capable de déduire lorsqu'une série de supports d'angle droite est utilisé comme opérateur ou comme déclaration de clôture de typedef, sans avoir à ajouter un espace.
typedef std::map< int , std::map < int , std::map < int , int > > > cpp98LongTypedef;
typedef std::map< int , std::map < int , std::map < int , int >>> cpp11LongTypedef;
Les fonctions des membres peuvent désormais être qualifiées selon que *this
s'agit d'une référence Lvalue ou Rvalue.
struct Bar {
// ...
};
struct Foo {
Bar& getBar () & { return bar; }
const Bar& getBar () const & { return bar; }
Bar&& getBar() && { return std::move (bar); }
const Bar&& getBar() const && { return std::move (bar); }
private:
Bar bar;
};
Foo foo{};
Bar bar = foo.getBar(); // calls `Bar& getBar() &`
const Foo foo2{};
Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&`
Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo).getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo2).getBar(); // calls `const Bar&& Foo::getBar() const&`
C ++ 11 permet aux fonctions et aux lambdas une syntaxe alternative pour spécifier leurs types de retour.
int f () {
return 123 ;
}
// vs.
auto f () -> int {
return 123 ;
}
auto g = []() -> int {
return 123 ;
};
Cette fonctionnalité est particulièrement utile lorsque certains types de retour ne peuvent pas être résolus:
// NOTE: This does not compile!
template < typename T, typename U>
decltype (a + b) add(T a, U b) {
return a + b;
}
// Trailing return types allows this:
template < typename T, typename U>
auto add (T a, U b) -> decltype(a + b) {
return a + b;
}
Dans C ++ 14, decltype(auto) (C++14)
peut être utilisé à la place.
Le spécificateur noexcept
spécifie si une fonction pourrait lancer des exceptions. Il s'agit d'une version améliorée de throw()
.
void func1 () noexcept ; // does not throw
void func2 () noexcept ( true ); // does not throw
void func3 () throw(); // does not throw
void func4 () noexcept ( false ); // may throw
Les fonctions non lancées sont autorisées à appeler des fonctions potentiellement lancées. Chaque fois qu'une exception est lancée et que la recherche d'un gestionnaire rencontre le bloc le plus à l'extérieur d'une fonction non lancement, la fonction std :: terminée est appelée.
extern void f (); // potentially-throwing
void g () noexcept {
f (); // valid, even if f throws
throw 42 ; // valid, effectively a call to std::terminate
}
Fournit des types standard pour représenter les chaînes UTF-8.
char32_t utf8_str[] = U" u0123 " ;
char16_t utf8_str[] = u" u0123 " ;
C ++ 11 introduit une nouvelle façon de déclarer des littéraux de cordes comme des "littéraux de cordes brutes". Les caractères émis à partir d'une séquence d'évasion (onglets, flux de lignes, barreaux simples, etc.) peuvent être entrés bruts tout en préservant le formatage. Il est utile, par exemple, d'écrire du texte littéraire, qui peut contenir beaucoup de citations ou de formatage spécial. Cela peut rendre vos littéraux de cordes plus faciles à lire et à maintenir.
Un littéral de chaîne brute est déclaré en utilisant la syntaxe suivante:
R"delimiter(raw_characters)delimiter"
où:
delimiter
est une séquence facultative de caractères composée de n'importe quel caractère source, à l'exception des parenthèses, des barreaux et des espaces.raw_characters
est toute séquence de caractères brute; ne doit pas contenir la séquence de fermeture ")delimiter"
.Exemple:
// msg1 and msg2 are equivalent.
const char * msg1 = " n Hello, nt world! n " ;
const char * msg2 = R"(
Hello,
world!
)" ;
std::move
indique que l'objet qui lui est transmis peut faire transférer ses ressources. L'utilisation d'objets qui ont été déplacés devraient être utilisés avec soin, car ils peuvent être laissés dans un état non spécifié (voir: que puis-je faire avec un objet déplacé à partir?).
Une définition de std::move
(effectuer un mouvement n'est rien de plus que de couler à une référence de référence):
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
Transfert de std::unique_ptr
S:
std::unique_ptr< int > p1 { new int { 0 }}; // in practice, use std::make_unique
std::unique_ptr< int > p2 = p1; // error -- cannot copy unique pointers
std::unique_ptr< int > p3 = std::move(p1); // move `p1` into `p3`
// now unsafe to dereference object held by `p1`
Renvoie les arguments qui y sont transmis tout en conservant leur catégorie de valeur et leur cv-qualifateurs. Utile pour le code et les usines génériques. Utilisé en conjonction avec forwarding references
.
Une définition de std::forward
:
template < typename T>
T&& forward( typename remove_reference<T>::type& arg) {
return static_cast <T&&>(arg);
}
Un exemple d'un wrapper
de fonction qui transmet simplement d'autres A
à un nouveau constructeur de copie ou de déplacement d' A
objet:
struct A {
A () = default ;
A ( const A& o) { std::cout << " copied " << std::endl; }
A (A&& o) { std::cout << " moved " << std::endl; }
};
template < typename T>
A wrapper (T&& arg) {
return A{std::forward<T>(arg)};
}
wrapper (A{}); // moved
A a;
wrapper (a); // copied
wrapper (std::move(a)); // moved
Voir aussi: forwarding references
, rvalue references
.
La bibliothèque std::thread
fournit un moyen standard de contrôler les threads, tels que les engener et les tuer. Dans l'exemple ci-dessous, plusieurs threads sont engendrés pour effectuer différents calculs, puis le programme les attend tous.
void foo ( bool clause) { /* do something... */ }
std::vector<std::thread> threadsVector;
threadsVector.emplace_back([]() {
// Lambda function that will be invoked
});
threadsVector.emplace_back(foo, true ); // thread will run foo(true)
for ( auto & thread : threadsVector) {
thread. join (); // Wait for threads to finish
}
Convertit un argument numérique en une std::string
.
std::to_string ( 1.2 ); // == "1.2"
std::to_string ( 123 ); // == "123"
Les traits de type définissent une interface basée sur des modèles de compilation pour interroger ou modifier les propriétés des types.
static_assert (std::is_integral< int >::value);
static_assert (std::is_same< int , int >::value);
static_assert (std::is_same<std::conditional< true , int , double >::type, int >::value);
C ++ 11 présente de nouveaux pointeurs intelligents: std::unique_ptr
, std::shared_ptr
, std::weak_ptr
. std::auto_ptr
est désormais obsolète, puis finalement supprimé dans C ++ 17.
std::unique_ptr
est un pointeur mobile non copieux qui gère sa propre mémoire allouée au tas. Remarque: Préférez l'utilisation des fonctions std::make_X
Helper plutôt que l'utilisation de constructeurs. Voir les sections pour std :: Make_unique et std :: Make_shared.
std::unique_ptr<Foo> p1 { new Foo{} }; // `p1` owns `Foo`
if (p1) {
p1-> bar ();
}
{
std::unique_ptr<Foo> p2 { std::move (p1)}; // Now `p2` owns `Foo`
f (*p2);
p1 = std::move (p2); // Ownership returns to `p1` -- `p2` gets destroyed
}
if (p1) {
p1-> bar ();
}
// `Foo` instance is destroyed when `p1` goes out of scope
Un std::shared_ptr
est un pointeur intelligent qui gère une ressource partagée entre plusieurs propriétaires. Un pointeur partagé contient un bloc de contrôle qui a quelques composants tels que l'objet géré et un compteur de référence. Toute l'accès au bloc de contrôle est une file d'attente, cependant, la manipulation de l'objet géré lui-même n'est pas une file d'attente.
void foo (std::shared_ptr<T> t) {
// Do something with `t`...
}
void bar (std::shared_ptr<T> t) {
// Do something with `t`...
}
void baz (std::shared_ptr<T> t) {
// Do something with `t`...
}
std::shared_ptr<T> p1 { new T{}};
// Perhaps these take place in another threads?
foo (p1);
bar (p1);
baz (p1);
La bibliothèque Chrono contient un ensemble de fonctions et de types utilitaires qui traitent des durées , des horloges et des moments . Un cas d'utilisation de cette bibliothèque est le code d'analyse comparative:
std::chrono::time_point<std::chrono::steady_clock> start, end;
start = std::chrono::steady_clock::now();
// Some computations...
end = std::chrono::steady_clock::now();
std::chrono::duration< double > elapsed_seconds = end - start;
double t = elapsed_seconds.count(); // t number of seconds, represented as a `double`
Les tuples sont une collection de tailles fixe de valeurs hétérogènes. Accédez aux éléments d'un std::tuple
en déballant à l'aide std::tie
, ou en utilisant std::get
.
// `playerProfile` has type `std::tuple<int, const char*, const char*>`.
auto playerProfile = std::make_tuple( 51 , " Frans Nielsen " , " NYI " );
std::get< 0 >(playerProfile); // 51
std::get< 1 >(playerProfile); // "Frans Nielsen"
std::get< 2 >(playerProfile); // "NYI"
Crée un tuple de références Lvalue. Utile pour déballer std::pair
et std::tuple
objets. Utilisez std::ignore
comme espace réservé pour les valeurs ignorées. Dans C ++ 17, les liaisons structurées doivent être utilisées à la place.
// With tuples...
std::string playerName;
std::tie (std::ignore, playerName, std::ignore) = std::make_tuple( 91 , " John Tavares " , " NYI " );
// With pairs...
std::string yes, no;
std::tie (yes, no) = std::make_pair( " yes " , " no " );
std::array
est un conteneur construit sur un tableau de style C. Prend en charge les opérations de conteneurs communes telles que le tri.
std::array< int , 3 > a = { 2 , 1 , 3 };
std::sort (a.begin(), a.end()); // a == { 1, 2, 3 }
for ( int & x : a) x *= 2 ; // a == { 2, 4, 6 }
Ces conteneurs maintiennent une complexité moyenne de temps constant pour les opérations de recherche, d'insertion et de suppression. Afin d'atteindre une complexité à temps constant, sacrifie l'ordre de vitesse en hachant les éléments en seaux. Il y a quatre conteneurs non ordonnés:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
std::make_shared
est le moyen recommandé de créer des instances de std::shared_ptr
s pour les raisons suivantes:
new
opérateur.foo
tel: foo (std::shared_ptr<T>{ new T{}}, function_that_throws(), std::shared_ptr<T>{ new T{}});
Le compilateur est libre d'appeler new T{}
, alors function_that_throws()
, et ainsi de suite ... puisque nous avons alloué des données sur le tas dans la première construction d'un T
, nous avons introduit une fuite ici. Avec std::make_shared
, nous avons la sécurité d'exception:
foo (std::make_shared<T>(), function_that_throws(), std::make_shared<T>());
std::shared_ptr{ new T{} }
, nous devons allouer de la mémoire à T
, puis dans le pointeur partagé, nous devons allouer de la mémoire au bloc de contrôle dans le pointeur. Voir la section sur les pointeurs intelligents pour plus d'informations sur std::unique_ptr
et std::shared_ptr
.
std::ref(val)
est utilisé pour créer un objet de type std::reference_wrapper
qui contient la référence de Val. Utilisé dans les cas où la référence habituelle passe en utilisant &
ne compile pas ou &
est supprimée en raison de la déduction de type. std::cref
est similaire mais Created Reference Wrapper détient une référence const à Val.
// create a container to store reference of objects.
auto val = 99 ;
auto _ref = std::ref(val);
_ref++;
auto _cref = std::cref(val);
// _cref++; does not compile
std::vector<std::reference_wrapper< int >>vec; // vector<int&>vec does not compile
vec.push_back(_ref); // vec.push_back(&i) does not compile
cout << val << endl; // prints 100
cout << vec[ 0 ] << endl; // prints 100
cout << _cref; // prints 100
C ++ 11 introduit un modèle de mémoire pour C ++, ce qui signifie la prise en charge de la bibliothèque pour les opérations de filetage et de atomique. Certaines de ces opérations incluent (mais ne sont pas limitées) les charges / magasins atomiques, les drapeaux atomiques, les promesses, les futures, les verrous et les variables de condition.
Voir les sections sur: std :: thread
std::async
exécute la fonction donnée soit de manière asynchrone, soit-évaluée, puis renvoie un std::future
qui tient le résultat de cet appel de fonction.
Le premier paramètre est la politique qui peut être:
std::launch::async | std::launch::deferred
est à la hauteur de la mise en œuvre d'effectuer une exécution asynchrone ou une évaluation paresseuse.std::launch::async
Exécutez l'objet appelant sur un nouveau fil.std::launch::deferred
sur le fil actuel. int foo () {
/* Do something here, then return the result. */
return 1000 ;
}
auto handle = std::async(std::launch::async, foo); // create an async task
auto result = handle.get(); // wait for the result
std::begin
et std::end
Free Fonctions ont été ajoutés pour retourner génériquement Begin and End Endrators of a Container. Ces fonctions fonctionnent également avec des tableaux bruts qui n'ont pas de fonctions begin
et end
des membres.
template < typename T>
int CountTwos ( const T& container) {
return std::count_if ( std::begin (container), std::end (container), []( int item) {
return item == 2 ;
});
}
std::vector< int > vec = { 2 , 2 , 43 , 435 , 4543 , 534 };
int arr[ 8 ] = { 2 , 43 , 45 , 435 , 32 , 32 , 32 , 32 };
auto a = CountTwos(vec); // 2
auto b = CountTwos(arr); // 1
Anthony Calandra
Voir: https://github.com/anthonycalandra/modern-cpp-features/graphs/contributors
MIT