actu-image
Software engineering - 02 Mar 2026

Les nouveautés C++

Photo de Thibault Ricord-Marchal

Article rédigé par

Thibault Ricord-Marchal

Ingénieur Conseil Software Engineering

Développeur C++ et systèmes embarqués chez MARGO, j’interviens actuellement pour la BNP CIB. Curieux de nature, je partage ici ma veille et mon expertise sur l’évolution des langages de programmation.

Mise à jour (Mai 2026) : La sortie de GCC 16.1 marque une étape historique. À peine 8 mois après la finalisation de la liste des nouveautés par le comité, le compilateur implémente déjà 80% des fonctionnalités de C++26, dont la réflexion statique et les contrats !

Vers une révolution du C++ ?

Depuis 2017, le C++ essaie de se rapprocher de plus en plus vers les sémantiques d’un langage moderne. En effet, l’arrivée de Rust dans l’écosystème aux alentours de 2015, avec la popularité qu’il a obtenue, a levé une prise de conscience sur le besoin d’avoir un langage plus sécurisé tout en conservant de la performance. De plus, début 2024, la Maison Blanche a fait un communiqué dans lequel elle demande aux développeurs de ne plus utiliser les langages C et C++ car trop peu sécurisés

C’est dans ce contexte que s’inscrit cette mise à jour du C++:

  • Rattraper un retard par rapport à d’autres langages.
  • Apporter plus de sécurité dans le langage pour répondre aux alertes de la Maison Blanche, en posant les premières briques vers une véritable « Memory Safety » (sécurité de la mémoire).

Contenu de la mise à jour

Voici les points les plus importants de cette mise à jour du langage:

  • Ajout de la réflexion statique
  • Ajout des contrats
  • Ajout de std::execution
  • Durcissement de la STL et création d’une notion de « Comportement erroné » pour remplacer certains cas de « Comportement indéfini »
  • Élargissement du constexpr sur tout le langage

Ajout de la réflexion statique

La réflexion en programmation, c’est la capacité pour un langage de s’observer lui-même (comme dans un miroir). De nombreux langages le proposent, comme Rust ou C#. Cette réflexion peut être faite au temps de l’exécution, on parle alors de réflexion dynamique. C’est le choix de Python et d’une partie du C# par exemple. Cela apporte plus de possibilités, mais au coût de la performance. L’autre solution est de résoudre la réflexion au temps de la compilation. On parle alors de réflexion statique, et c’est le choix fait par Rust, ainsi que par C++. Cela rend l’implémentation compliquée, mais on lève totalement le coût en performance.

Voici un très bref résumé de comment cela fonctionne en C++. Pour obtenir la réflexion d’une entité dans le code, on utilise l’opérateur de réflexion ^^. Cela nous donne un objet constexpr de type std::meta::info. Un objet de type std::meta::info peut représenter n’importe quoi, y compris: un namespace, une classe, un type, une fonction, une variable, etc. Ensuite, le header <meta> expose des fonctions consteval pour récupérer des informations sur ce std::meta::info. Une fois le traitement terminé, on utilise l’opérateur de recollage (splice operator) [: :] pour obtenir une entité réelle. Par exemple:

int main() { constexpr std::meta::info int_type_info = ^^int; // Ici, int_type_info contient toutes les informations du type int. // Par exemple, le nom du type sous forme de std::string_view static_assert(std::meta::identifier_of(int_type_info) == « int »); // On peut ensuite repasser au « vrai » type avec le splice operator [: :] [:int_type_info:] my_int = 42; // « [:int_type_info:] » est remplacé par « int » return my_int; }

Bien évidemment, cet exemple est basique, et ne montre pas toutes les possibilités. Donc voici une liste d’une partie des possibilités de la réflexion:

  • Sérialisation et désérialisation automatiques des objets
  • Inspection en profondeur des types pour des Concepts toujours plus poussés (par exemple un concept has_only_public_members)
  • Des facilités sécurisées de casts
  • Une nouvelle façon de rendre du code générique

J’ai travaillé avec cette réflexion pour produire une petite bibliothèque qui montre un peu les possibilités. Voici le lien github: https://github.com/NoliaXeh/metaref

Ajout des contrats

Dans cet objectif de rajouter de la sécurité à C++, le comité a adopté l’ajout des contrats dans le C++, une demande présente depuis des années, qui aurait pu arriver dès C++20, mais a finalement été adoptée que récemment.

L’idée est d’ajouter la possibilité de faire de la programmation par contrat au C++. La programmation par contrat, c’est un paradigme qui consiste à offrir des garanties autour d’une fonction, de façon claire et vérifiable, pour s’assurer de l’absence d’erreurs de programmation classiques. La programmation par contrat apporte généralement trois types de vérifications, et ce sont ceux-ci qui ont été ajoutés au C++:

  • La précondition : Elle vérifie une condition juste au moment de l’appel d’une fonction. Si la condition est fausse, alors on stoppe le programme et on affiche une erreur.
  • La post-condition: Elle vérifie une condition juste au moment du retour d’une fonction. Si la condition est fausse, alors on stoppe le programme et on affiche une erreur.
  • L’assertion: Comme la macro assert, elle vérifie une condition au milieu d’une fonction. Si la condition est fausse, alors on stoppe le programme et on affiche une erreur.

Voici un exemple simple:

double square_root(double x)
    pre(x >= 0)         // Si x est négatif, on ne peut pas calculer sa racine carrée
    post(r: r * r == x) // On assure que la valeur de retour au carré donne X (ici, r est la valeur de retour)
{
    /// Code ici
}

int main() {
    double value = square_root(9);
    contract_assert(value == 3); // Contrat vérifié dans un corps de fonction
    std::println("sqrt(9) = {}", value);
}

C’est un immense progrès pour la sécurité du langage. Certains l’auront intuité: cela va avoir un coût en performance de vérifier ces choses à l’exécution. C’est vrai, et c’est pour cela que nous avons plusieurs politiques de vérification des contrats:

  • Ignore : On ignore totalement les contrats, ils ne sont pas compilés et n’ont alors plus aucun coût
  • Observe: On exécute les contrats, mais on ne plante pas en cas d’erreur, on fait un log
  • Enforce: On exécute les contrats, et on lève une exception en cas d’erreur
  • Quick-enforce: On exécute les contrats, et on stoppe le programme en cas d’erreur (pas d’exception)

Les contrats tels qu’ajoutés sont sujets à de vives critiques, notamment sur le fait qu’on décide de les ignorer, ou qu’ils posent un souci de risque de plantage dans des systèmes critiques.

La révolution asynchrone : std::execution (Senders & Receivers)

C’est probablement l’un des plus gros morceaux de C++26. Historiquement, la programmation asynchrone et le multithreading en C++ ont toujours manqué d’un modèle standard unifié. Nous avions std::thread (souvent trop bas niveau) ou la paire std::async / std::future (qui pose de sérieux problèmes de performance en bloquant parfois les threads de manière inattendue).

Avec l’adoption de la proposition P2300, C++26 introduit std::execution, un framework standard basé sur le paradigme des Senders (émetteurs) et Receivers (récepteurs).

L’idée maîtresse est de séparer strictement la description du travail à accomplir de l’endroit où ce travail s’exécute. Ce modèle s’articule autour de trois concepts clés :

  • std::execution::sender: Ils décrivent une opération asynchrone. Par nature, ils sont “paresseux” (lazy) : définir un Sender ne lance aucune exécution tant qu’on ne le connecte pas explicitement.
  • std::execution::receiver: Ils sont les destinataires du résultat d’un Sender. Ils prévoient trois canaux (succès avec une valeur, échec avec une erreur, ou annulation pure et simple).
  • std::execution::scheduler: Ils définissent le contexte d’exécution (un thread pool, une boucle d’événements, une tâche système ou même un GPU).
Zero-overhead architecture

L’avantage majeur de ce modèle est sa composabilité. Vous pouvez chaîner des opérations complexes à l’aide de pipes (l’opérateur |), sans jamais tomber dans l’enfer des callbacks d’erreurs. De plus, l’architecture est pensée pour le zero-overhead: le compilateur est capable d’aplatir toute cette chaîne d’exécution lors de la compilation pour générer un code assembleur extrêmement optimisé et sans allocations mémoire inutiles.

Durcissement de la STL et comportement erroné

Toujours dans cet objectif de rendre le langage plus sécurisé, il a fallu se pencher sur la STL qui a fait plusieurs choix de design pour privilégier les performances au coût de la sécurité. Par exemple, les containers n’ont pas de vérification de dépassement quand on y accède via l’operator[]. Avant C++26, faire ce genre d’accès était qualifié de “comportement indéfini”.

Depuis C++ 26, ces comportements indéfinis qui causent des erreurs de sécurité sont requalifiés de comportements erronés (erroneous behaviour). Cela permet aux compilateurs de choisir une politique de gestion différente en fonction de leur configuration, par exemple en appliquant des contrats sur ces comportements lorsqu’on compile en mode debug.

Aparté : Le C++26 est-il devenu “Memory Safe” comme Rust ?


Il est important de faire la distinction entre la sécurité globale apportée par C++26 et la stricte Memory Safety réclamée par la NSA ou la Maison Blanche.


Les contrats et les comportements erronés de C++26 règlent d’énormes problèmes (comme la lecture de variables non initialisées ou les dépassements de tableaux, qui touchent à la sécurité spatiale). Cependant, ils ne résolvent pas magiquement les problèmes de sécurité temporelle liés à la gestion de la durée de vie des objets en mémoire, comme les fameux Use-After-Free (utiliser un pointeur après sa libération) ou les Dangling Pointers.


C++26 fait un pas de géant vers un code plus robuste, mais pour atteindre le niveau de garantie d’un borrow checker à la Rust lors de la compilation, il faudra attendre les futures évolutions du langage (notamment le concept très attendu des C++ Profiles soutenu par Bjarne Stroustrup).

Élargissement du constexpr sur tout le langage

Désormais, la totalité du langage peut être évaluée au compile-time. Déjà, en C++23, une grande partie du langage devenait constexpr-compatible, mais C++26 ajoute notamment le placement new et la conversion de pointeurs void* vers d’autres types de pointeurs.

Les quelques petites choses qui ne méritaient pas un paragraphe

  • Ajout de la wild-card _ pour désigner une variable ignorée.
  • Ajout des symboles $, @ et « ` dans le basic character set du langage.
  • L’ajout d’une directive #embed "filename" pour ajouter un fichier dans le code en tant que char[].
  • = delete peut maintenant avoir un message d’erreur (ex: =delete("Message")).
  • std::hive, un tout nouveau container qui respecte la sémantique d’un bucket-array.
Expertise C++

En conclusion : Une véritable révolution pour le C++ ?

Alors, le C++26 marque-t-il vraiment cette révolution annoncée en introduction ? Au vu de ces nouveautés, il est difficile d’affirmer le contraire.

Pendant des décennies, le langage a privilégié la performance pure et la flexibilité, laissant au développeur la lourde responsabilité d’esquiver les fameux comportements indéfinis. Aujourd’hui, avec l’introduction des contrats et de la notion de comportement erroné, le comité standard opère un virage sécuritaire assumé. C’est une réponse directe et concrète aux exigences de l’industrie moderne et aux avertissements d’institutions comme la Maison Blanche.

En parallèle, l’arrivée historique de la réflexion statique couplée à l’élargissement massif du constexpr va transformer radicalement la façon dont nous architecturons nos bibliothèques. La sérialisation devient triviale, le code gagne en expressivité, et la métaprogrammation devient enfin lisible, reléguant au passé les macros complexes et les template cryptiques.

De plus, en standardisant enfin un modèle robuste pour l’asynchronisme avec std::execution (le paradigme des Senders et Receivers), le langage comble l’une de ses plus grandes lacunes historiques. Le C++ standard offre désormais un framework unifié et composable pour orchestrer la concurrence complexe, reléguant au second plan les anciens outils souvent limités ou coûteux en performances.

Le C++26 ne se contente pas de rattraper son retard sur des langages plus récents comme Rust ou C#. Il prouve sa capacité à se moderniser tout en respectant son ADN : offrir des abstractions à coût d’exécution nul. Plus qu’une simple évolution de la norme, c’est un véritable renouveau qui redonne au C++ toutes les armes pour rester un leader incontournable dans les décennies à venir.


Vous souhaitez approfondir les sujets liés au C++26 ou auditer votre base de code ?

Contacter un expert MARGO