Xavier Lamorlette
Sommaire :
consteval
et constinit
<=>
operatorlikely
et unlikely
nodiscard
for
avec initialisationusing enum
Regular
et SemiRegular
std::range
std::views
std::format
std::span
constexpr
std::string
std::stringstream
contains
erase
source_location
std::jthread
consteval
et constinit
consteval
qualifie une fonction qui est évaluée exclusivement à la compilation (“immediate function”).
consteval int increment(int n) {
return n + 1;
}
constinit
qualifie une variable qui est initialisée à la compilation (mais qui n'est pas nécessairement constante).
constinit int a = increment(1);
a ++;
On peut désormais utiliser des méthodes virtuelles dans des expressions constantes.
class My_class {
public:
int a = 0;
int b = 0;
int c = 0;
};
{
My_class object{.a = 1, .c = 3};
}
<=>
operatorComparaison trilatérale (“three-way comparison operator”) :
(a <=> b) < 0
(a <=> b) == 0
(a <=> b) > 0
Cet opérateur doit être une relation d'ordre total (weak ordering), c'est-à-dire vérifiant (a == b || a < b || a > b) == true
.
==
signifie ici que les éléments sont équivalents mais pas forcément identiques.
Avec une relation d'ordre strict total (strict weak ordering), ==
signifie que les éléments sont identiques.
Cet opérateur peut être construit automatiquement :
#include <compare>
class A {
auto operator <=> (const A &) const = default;
};
likely
et unlikely
if (condition) [[likely]] {
…
} else [[unlikely]] {
…
}
nodiscard
Indique que la valeur de retour ne doit pas être ignorée.
for
avec initialisationfor (std::vector<A> collection = toto(); const A & item: collection) {…}
for (size_t index = 0; const A & item: collection) {
…
index++;
}
using enum
enum class Toto: uint32_t {
First = 0
Second
};
using enum Toto;
Toto toto = First;
Les concepts permettent de définir sémantiquement les contraintes de validité des paramètres templates.
Ce sont des prédicats booléens, nommés, sur des paramètres templates, évalués à la compilation.
La surcharge avec des concepts permet de remplacer les traits et les enable_if
.
Les concepts peuvent être utilisés pour la résolution de surcharge au lieu du SFINAE.
Exemples :
template <typename T>
concept Integral = std::is_integral<T>::value;
template <typename T>
concept Addable = requires(T a, T b) {
a + b;
};
template <typename T>
concept EqualityComparable = requires(T a, T b) {
{a == b} -> std::same_as<bool>;
{a != b} -> std::same_as<bool>;
};
template <EqualityComparable T>
bool are_equal(const T & a,
const T & b);
return a == b;
}
Remarque : les concepts ne contenant qu'une seule propriété ou contrainte sont suspects.
Une expression requires
teste si les paramètres templates fournissent les interfaces désirées, et renvoie un booléen :
template <typename Iterator>
struct Foo {
static_assert(requires(Iterator i) {i ++;}, "template parameter does not provide increment");
}
Tester un type sur un concept :
static_assert(Concept<My_type>);
L'utilisation d'un concept dans la signature d'une fonction rend cette fonction template :
void foo(Integral a);
// ...
}
Les concepts peuvent être utilisés au lieu du susbtitut non contraint auto
:
Sortable s = f(x); // au lieu de auto s = f(x);
requires
peut être mis avant ou après le nom :
template <typename T>
requires Integral<T>()
T foo(T x) {
…
}
template <typename T>
T foo(T x)
requires Integral<T>() {
…
}
Les syntaxes abrégées suivantes sont équivalentes à celles ci-dessus :
template <Integral T>
T foo(T x) {
…
}
Integral auto foo(Integral auto x) {
…
}
Regular
et SemiRegular
SemiRegular
est un concept équivalent à DefaultConstructible
+ Destructible
+ CopyConstructible
+ CopyAssignable
+ MoveConstructible
+ MoveAssignable
+ Swappable
.
Regular
est un concept équivalent à SemiRegular
+ EqualityComparable
.
Les containeurs et les algorithmes de la STL requièrent des types Regular.
Les coroutines sont des fonctions asynchrones, c'est-à-dire qui peuvent suspendre et reprendre leur exécution tout en conservant leur état.
Elles permettent de faire :
co_yield
suspend l'exécution d'une fonction et retourne une valeur.
Cela permet d'écrire des générateurs, avec évaluation retardée (appel par nécessité, “lazy evaluation”) :
std::generator<int> arithmetic_suite(int first_element,
int difference) {
for (int element = first_element; ; element += difference) {
co_yield element;
}
}
co_return
permet de retourner une valeur depuis une coroutine.
co_await
permet de suspendre et reprendre l'exécution en fonction d'une expression qui doit implémenter :
bool await_ready()
;
void await_suspend()
;
void await_resume()
.
Les modules permettent de :
#include
;// module interface: my_module.ixx
module;
import std.core;
export module my_module;
export namespace my_module {
int foo(int arg);
}
// module implementation: my_module.cpp
module my_module;
import std.core;
namespace my_module {
int foo(int arg) {
…
}
}
// client code
import std.core;
import my_module;
{
my_module::foo(1);
}
Le module std.core
contient quasiment toute la STL (notamment sauf std.memory
).
On peut mettre l'interface et l'implémentation dans le même fichier comme suit :
// my_module.ixx
module;
import std.core;
export module my_module;
export namespace my_module {
int foo(int arg);
}
module :private;
int foo(int arg) {
…
}
La bibliothèque Ranges permet d'appliquer directement des algorithmes sur des collections, sans passer par des itérateurs, dans un style de programmation fonctionnelle.
std::range
Un std::range
est un groupe d'éléments sur lequel on peut itérer.
Exemples :
const std::string_view value;
size_t length = std::ranges::distance(value);
std::ranges::count(value, 'x');
auto sub_string = std::ranges::subrange(std::ranges::find(value, 'x') + 1, std::ranges::end(value));
std::ranges::all_of(value, predicate);
std::views
Une vue est une adaptation composable d'un range, qui ne possède pas les données.
On compose les algorithmes grâce à l'opérateur pipe |
.
La fonction d'adaptation est appliquée lorsque l'on itère sur la view, par un mécansime d'évaluation retardée (“lazy evaluation”).
Cela permet notamment d'appliquer les algorithmes sur des flux infinis de données (streams ou pipelines).
std::views
est un alias de std::ranges::views
.
Exemples :
std::map<…, …> a_map;
std::views::keys(a_map)
std::views::values(a_map)
std::views::reverse
std::views::take(12)
std::views::filter(predicate)
std::views::transform(function)
std::vector<int> numbers = {1, 2, 3, 4};
std::vector<int> incremented_odd_numbers = numbers
| std::views::filter([](int n){return n % 2 == 1;})
| std::views::transform([](int n){ return n + 1;});
auto result = collection
| std::views::drop_while(predicate)
| std::views::take_while(predicate_2);
std::format
std::format
permet de formatter les chaînes de caractères.
Cela remplace notamment l'utilisation de iomanip
.
std::span
std::span
est une vue sur une séquence contigue d'objets.
Il n'est pas propriétaire de la mémoire. La mémoire contigue peut-être, par exemple, celle d'un tableau C, d'un array on d'un vector.
Ce peut être vu comme une généralisation de std::string_view
(avec la possibilité de modifier les éléments).
Ce peut être utile pour traiter en parallèle différentes parties d'une séquence.
constexpr
std::string
et std::vector
peuvent être constexpr
.
std::string
std::string::starts_with()
, std::string::ends_with()
std::stringstream
std::stringstream::view()
renvoie une std::string_view
.
contains
Les containeurs de la STL ont maintenant une méthode contains()
.
erase
Au lieu de l'idiome “erase - remove”, on a maintenant les fonctions std::erase(container, value)
et erase_if(container, predicate)
.
source_location
source_location::current()
renvoie un objet avec les membres file_name
, line
et function_name
.
std::jthread
Thread avec join
automatique en sortie de portée.
La dernière mise à jour de cette page date d'août 2024.
Le contenu de ce site est, en tant qu'œuvre originale de l'esprit, protégé par le droit d'auteur.
Pour tout commentaire, vous pouvez m'écrire à xavier.lamorlette@gmail.com.