🔝 Retour au Sommaire
🎯 Niveau : Intermédiaire
Ce module couvre les structures de données et les algorithmes de la STL — le code que vous n'avez pas à écrire vous-même. Conteneurs séquentiels (std::vector, std::array, std::span), conteneurs associatifs (std::map, std::unordered_map, std::flat_map), algorithmes classiques et parallèles, ranges (C++20), et enfin templates avec concepts (C++20) pour écrire du code générique contraint. Le choix du bon conteneur et du bon algorithme a plus d'impact sur la performance que la plupart des micro-optimisations.
- Choisir le conteneur STL adapté à un problème en fonction de la complexité algorithmique, de la localité mémoire et des patterns d'accès.
- Utiliser
std::span(C++20) comme vue zero-copy sur des données contiguës, en remplacement des paramètres pointeur+taille. - Appliquer les algorithmes STL (recherche, tri, transformation, manipulation) avec des lambdas et des itérateurs appropriés.
- Composer des pipelines de traitement avec les ranges (C++20) et l'opérateur
|. - Paralléliser des algorithmes STL avec les politiques d'exécution (
std::execution::par,par_unseq). - Implémenter des templates de fonctions et de classes, avec spécialisation partielle/totale, variadic templates, et fold expressions.
- Contraindre des templates avec des concepts (C++20), y compris la création de concepts personnalisés avec la syntaxe
requires.
- Module 4, chapitre 9 : smart pointers — les conteneurs STL stockent souvent des
unique_ptroushared_ptr, et le transfert de propriété viastd::moveest courant. - Module 4, chapitre 10 : move semantics —
emplace_back,push_back(std::move(...)), et le comportement des conteneurs lors de la réallocation reposent sur la move semantics. - Module 4, chapitre 11 : lambdas — les algorithmes STL s'utilisent principalement avec des lambdas comme prédicats ou transformations.
- Module 4, chapitre 12 : introduction aux concepts et aux ranges — ce module les approfondit en contexte.
Les conteneurs qui stockent des éléments dans un ordre déterminé par l'insertion. std::vector est le conteneur par défaut — les autres existent pour des cas d'usage spécifiques où vector est sous-optimal.
std::vector: fonctionnement interne (buffer contigu, capacité vs taille, facteur de croissance), méthodes essentielles (push_back,emplace_back,reserve,shrink_to_fit), invalidation des itérateurs après réallocation.std::array: tableau de taille fixe, stocké sur la stack, zéro overhead par rapport à un C-array.std::listetstd::forward_list: listes doublement/simplement chaînées — itérateurs jamais invalidés, mais mauvaise localité mémoire.std::deque: file à double entrée — insertion O(1) aux deux bouts, accès aléatoire O(1), mais mémoire non contiguë.std::span(C++20) : vue non-owning sur des données contiguës — span statique (std::span<int, 5>) vs dynamique (std::span<int>), remplacement du pattern(T* ptr, size_t len), interopérabilité avecvector,arrayet C-arrays.- Complexité algorithmique (Big O) et guide de choix du conteneur selon le pattern d'accès.
Les conteneurs qui associent des clés à des valeurs ou qui maintiennent un ensemble unique. Le choix entre ordered, unordered et flat dépend du volume de données, du pattern d'accès et des contraintes de localité mémoire.
std::mapetstd::multimap: arbres rouge-noir, accès O(log n), clés triées — adapté aux parcours ordonnés.std::unordered_map: table de hachage, accès O(1) amorti — dépend de la qualité de la fonction de hash. Custom hash functions pour les types personnalisés.std::setetstd::unordered_set: mêmes structures sous-jacentes quemap/unordered_map, sans valeur associée.std::flat_mapetstd::flat_set(C++23) : conteneurs ordonnés à mémoire contiguë — meilleures performances questd::mappour les lectures fréquentes sur de petits à moyens ensembles de données grâce à la localité cache.- Comparaison de performances : ordered vs unordered vs flat — benchmarks et critères de décision.
Les algorithmes opèrent sur des ranges d'itérateurs et ne connaissent pas les conteneurs qui les hébergent. Ce chapitre couvre les algorithmes classiques, l'introduction des ranges (C++20), et la parallélisation.
- Recherche :
std::find,std::binary_search,std::lower_bound— pré-conditions de tri pour les recherches binaires. - Tri :
std::sort(introsort),std::stable_sort(mergesort),std::partial_sort. - Transformation :
std::transform(map),std::accumulate/std::reduce(fold). - Manipulation :
std::copy,std::move,std::remove_if— attention :std::remove_ifne supprime pas, il déplace les éléments non-matchés vers le début et retourne un itérateur de fin. - Itérateurs : les cinq catégories (input, output, forward, bidirectional, random_access) et leur relation avec les algorithmes.
- Ranges (C++20) : views et lazy evaluation, composition de pipelines avec
|(views::filter,views::transform,views::take). - Algorithmes parallèles : politiques d'exécution
std::execution::seq,par,par_unseq,unseq— parallélisation destd::sort,std::transform,std::reduce. Précautions : data races, overhead de threading, limitations selon l'implémentation.
Les templates sont le mécanisme de généricité de C++. Ce chapitre couvre les templates classiques, les techniques avancées (SFINAE, variadic templates), et leur remplacement moderne par les concepts (C++20).
- Templates de fonctions et de classes : syntaxe, déduction de type, instanciation explicite.
- Spécialisation partielle et totale : personnaliser le comportement d'un template pour des types spécifiques.
- SFINAE (Substitution Failure Is Not An Error) : mécanisme historique pour activer/désactiver des overloads selon les propriétés d'un type — messages d'erreur souvent cryptiques.
- Variadic templates (C++11) : templates avec un nombre variable de paramètres (
template<typename... Args>), expansion de pack. - Concepts (C++20) : syntaxe
requires, concepts standard de la STL (std::integral,std::floating_point,std::sortable,std::ranges::range), création de concepts personnalisés — remplacement de SFINAE avec des messages d'erreur lisibles. - Fold expressions (C++17) : réduction d'un parameter pack en une seule expression (
(args + ...)).
-
Invalidation d'itérateurs après modification d'un
std::vector. Unpush_backqui déclenche une réallocation invalide tous les itérateurs, pointeurs et références vers les éléments du vector. C'est un des bugs les plus fréquents en C++. Si vous itérez sur un vector, n'insérez pas dedans. Si vous devez insérer pendant un parcours, utilisez l'idiomeerase-removeou travaillez par indices. Règle pratique : appelezreserve()si vous connaissez la taille finale. -
std::unordered_mapavec une fonction de hash mal distribuée. Si trop de clés tombent dans le même bucket, les opérations passent de O(1) amorti à O(n) par bucket. Avec une hash triviale (par exemple, hasher un entier modulo une petite constante), les performances peuvent être pires qu'unstd::map. Utilisez les hash functions de la STL pour les types standard et implémentez des hash de qualité (FNV-1a, combinaison avecstd::hash) pour les types personnalisés. -
Confondre
std::remove/std::remove_ifavec une suppression réelle.std::remove_ifne réduit pas la taille du conteneur — il déplace les éléments non-matchés vers le début et retourne un itérateur vers la nouvelle fin logique. Sans appel àeraseensuite, les éléments "supprimés" restent dans le conteneur. L'idiome correct estv.erase(std::remove_if(v.begin(), v.end(), pred), v.end());— ou avec C++20,std::erase_if(v, pred)qui fait les deux en un. -
SFINAE vs Concepts : savoir quand utiliser lequel. SFINAE reste nécessaire si vous devez supporter du code pre-C++20 ou si vous maintenez une librairie qui cible C++17. Pour tout code nouveau ciblant C++20 ou ultérieur, les concepts sont supérieurs : messages d'erreur clairs, syntaxe lisible, composition avec
&&et||. Ne mélangez pas SFINAE et concepts dans la même overload set — choisissez l'un ou l'autre.
À l'issue de ce module, vous savez :
- Choisir le bon conteneur STL selon la complexité algorithmique et la localité mémoire, y compris les nouveaux
std::flat_map/std::flat_set(C++23). - Utiliser
std::spancomme interface de fonction pour les données contiguës, en remplacement des pointeurs bruts. - Appliquer les algorithmes STL avec lambdas, et composer des pipelines de traitement avec les ranges (C++20).
- Paralléliser des algorithmes via
std::execution::paren tenant compte des contraintes de thread-safety. - Écrire des templates génériques contraints par des concepts (C++20) avec des messages d'erreur exploitables.
- Implémenter des variadic templates et des fold expressions pour du code générique à nombre variable d'arguments.