actu-image
Software engineering - 27 Feb 2026

What's new in C++

Thibault Ricord-Marchal's photo

Article written by

Thibault Ricord-Marchal

Software Engineering Consulting Engineer

C++ and embedded systems developer at MARGO, I am currently working for BNP CIB. Naturally curious, I share here my watch and expertise on the evolution of programming languages.

Towards a C++ revolution?

Since 2017, C++ has been trying to move closer and closer to modern language semantics. Indeed, the arrival of Rust in the ecosystem around 2015, along with the popularity it gained, raised awareness about the need for a more secure language while maintaining performance. Furthermore, in early 2024, the White House issued a statement in which it urges developers to no longer use C and C++ languages because they are not secure enough.

It is in this context that this C++ update takes place:

  • Catching up with other languages.
  • Bringing more security to the language to respond to White House alerts, by laying the first bricks towards true « Memory Safety ».

Update content

Here are the most important points of this language update:

  • Addition of static reflection
  • Addition of contracts
  • Addition of std::execution
  • Hardening of the STL and creation of an « Erroneous behaviour » notion to replace certain cases of « Undefined behaviour »
  • Expansion of constexpr across the whole language

Adding static reflection

Reflection in programming is the ability for a language to observe itself (like in a mirror). Many languages offer it, such as Rust or C#. This reflection can be done at runtime, which is called dynamic reflection. This is the choice made by Python and part of C#, for example. It brings more possibilities, but at the cost of performance. The other solution is to resolve reflection at compile time. This is called static reflection, and it is the choice made by Rust, as well as by C++. This makes the implementation complicated, but the performance cost is completely removed.

Here is a very brief summary of how it works in C++. To obtain the reflection of an entity in the code, the reflection operator ^^ is used. This gives us a constexpr object of type std::meta::info. An object of type std::meta::info can represent anything, including: a namespace, a class, a type, a function, a variable, etc. Then, the <meta> header exposes consteval functions to retrieve information on this std::meta::info. Once the processing is finished, the splice operator [: :] is used to obtain a real entity. For example:

int main() { constexpr std::meta::info int_type_info = ^^int; // Here, int_type_info contains all information of the int type. // For example, the name of the type as a std::string_view static_assert(std::meta::identifier_of(int_type_info) == “int”); // We can then go back to the “real” type with the splice operator [: :] [:int_type_info:] my_int = 42; // “[:int_type_info:]” is replaced by “int” return my_int; }

Obviously, this example is basic and does not show all the possibilities. So here is a list of some of the reflection possibilities:

  • Automatic serialization and deserialization of objects
  • In-depth inspection of types for increasingly advanced Concepts (for example, a has_only_public_members concept)
  • Secure cast facilities
  • A new way to make code generic

I worked with this reflection to produce a small library that shows some of the possibilities. Here is the GitHub link: https://github.com/NoliaXeh/metaref

Adding contracts

With this goal of adding security to C++, the committee adopted the addition of contracts in C++, a demand present for years, which could have arrived as early as C++20, but was only recently adopted.

The idea is to add the possibility of doing design by contract in C++. Design by contract is a paradigm that consists of offering guarantees around a function, in a clear and verifiable way, to ensure the absence of classic programming errors. Design by contract generally brings three types of checks, and these are the ones that have been added to C++:

  • Precondition: It checks a condition just at the moment a function is called. If the condition is false, then the program stops and an error is displayed.
  • Post-condition: It checks a condition just at the moment a function returns. If the condition is false, then the program stops and an error is displayed.
  • Assertion: Like the assert macro, it checks a condition in the middle of a function. If the condition is false, then the program stops and an error is displayed.

Here is a simple example:

double square_root(double x)
    pre(x >= 0)         // If x is negative, we cannot calculate its square root
    post(r: r * r == x) // We ensure that the return value squared equals X (here, r is the return value)
{
    /// Code here
}

int main() {
    double value = square_root(9);
    contract_assert(value == 3); // Contract checked in a function body
    std::println("sqrt(9) = {}", value);
}

This is a huge step forward for the security of the language. Some may have intuited it: checking these things at runtime will have a performance cost. It is true, and that is why we have several contract checking policies:

  • Ignore: Contracts are totally ignored, they are not compiled and therefore have no cost
  • Observe: Contracts are executed, but the program does not crash in case of error; instead, a log is created
  • Enforce: Contracts are executed, and an exception is thrown in case of error
  • Quick-enforce: Contracts are executed, and the program is stopped in case of error (no exception)

Contracts as added are subject to sharp criticism, notably on the fact that we can decide to ignore them, or that they pose a crash risk issue in critical systems.

The asynchronous revolution: std::execution (Senders & Receivers)

This is probably one of the biggest pieces of C++26. Historically, asynchronous programming and multithreading in C++ have always lacked a unified standard model. We had std::thread (often too low-level) or the pair std::async / std::future (which poses serious performance problems by sometimes blocking threads unexpectedly).

With the adoption of proposal P2300, C++26 introduces std::execution, a standard framework based on the Senders and Receivers paradigm.

The main idea is to strictly separate the description of the work to be accomplished from where that work is executed. This model revolves around three key concepts:

  • std::execution::sender: They describe an asynchronous operation. By nature, they are « lazy »: defining a Sender does not start any execution until it is explicitly connected.
  • std::execution::receiver: They are the recipients of a Sender’s result. They provide three channels (success with a value, failure with an error, or outright cancellation).
  • std::execution::scheduler: They define the execution context (a thread pool, an event loop, a system task or even a GPU).
Zero-overhead architecture

The major advantage of this model is its composability. You can chain complex operations using pipes (the | operator), without ever falling into error callback hell. Furthermore, the architecture is designed for zero-overhead: the compiler is able to flatten this whole execution chain during compilation to generate extremely optimized assembly code without unnecessary memory allocations.

Hardening of the STL and erroneous behaviour

Still with the objective of making the language more secure, it was necessary to look at the STL, which made several design choices to privilege performance over security. For example, containers do not have bounds checking when accessed via operator[]. Before C++26, performing this kind of access was classified as « Undefined behaviour ».

Since C++26, these undefined behaviours that cause security errors are reclassified as « Erroneous behaviour ». This allows compilers to choose a different management policy depending on their configuration, for example by applying contracts on these behaviours when compiling in debug mode.

Aside: Has C++26 become « Memory Safe » like Rust?


It is important to distinguish between the overall security brought by C++26 and the strict Memory Safety demanded by the NSA or the White House.


C++26 contracts and erroneous behaviours settle huge issues (like reading uninitialized variables or array overflows, which touch upon spatial security). However, they do not magically solve temporal security problems related to the management of object lifetimes in memory, such as the famous Use-After-Free (using a pointer after it has been freed) or Dangling Pointers.


C++26 takes a giant step towards more robust code, but to reach the level of guarantee of a Rust-like borrow checker during compilation, we will have to wait for future language evolutions (notably the highly anticipated C++ Profiles concept supported by Bjarne Stroustrup).

Expansion of constexpr across the whole language

From now on, the entire language can be evaluated at compile-time. Already, in C++23, a large part of the language was becoming constexpr-compatible, but C++26 adds notably the placement new and the conversion of void* pointers to other pointer types.

A few small things that didn’t deserve a paragraph

  • Addition of the wild-card _ to designate an ignored variable.
  • Addition of the symbols $, @ and “` in the language’s basic character set.
  • The addition of a #embed "filename" directive to add a file into the code as a char[].
  • = delete can now have an error message (e.g.: =delete("Message")).
  • std::hive, a brand new container that respects bucket-array semantics.
C++ Expertise

Conclusion: A true revolution for C++?

So, does C++26 really mark the revolution announced in the introduction? Given these new features, it is difficult to claim otherwise.

For decades, the language has privileged pure performance and flexibility, leaving the developer with the heavy responsibility of dodging the famous undefined behaviours. Today, with the introduction of contracts and the notion of erroneous behaviour, the standard committee is making an assumed security turn. This is a direct and concrete response to the requirements of modern industry and warnings from institutions like the White House.

In parallel, the historic arrival of static reflection coupled with the massive expansion of constexpr will radically transform the way we architect our libraries. Serialization becomes trivial, code gains in expressivity, and metaprogramming finally becomes readable, relegating complex macros and cryptic templates to the past.

Furthermore, by finally standardizing a robust model for asynchrony with std::execution (the Senders and Receivers paradigm), the language fills one of its greatest historical gaps. Standard C++ now offers a unified and composable framework for orchestrating complex concurrency, pushing old tools—often limited or performance-costly—to the background.

C++26 does not just catch up with newer languages like Rust or C#. It proves its ability to modernize while respecting its DNA: offering zero-cost abstractions. More than just an evolution of the standard, it is a true renewal that gives C++ all the weapons needed to remain an essential leader for decades to come.


Would you like to explore C++26 topics further or audit your codebase?

Contact a MARGO expert