Articles & Books

Reflection voted into C++26: "Whole new language" -- Herb Sutter

The first trip report from the Sofia meeting is available:

Trip report: June 2025 ISO C++ standards meeting (Sofia, Bulgaria)

by Herb Sutter

From the article:

c26-reflection.pngA unique milestone: “Whole new language”

Today marks a turning point in C++: A few minutes ago, the C++ committee voted the first seven (7) papers for compile-time reflection into draft C++26 to several sustained rounds of applause in the room. I think Hana “Ms. Constexpr” Dusíková summarized the impact of this feature best a few days ago, in her calm deadpan way… when she was told that the reflection paper was going to make it to the Saturday adoption poll, she gave a little shrug and just quietly said: “Whole new language.”

Mic drop.

C++26: Erroneous Behaviour -- Sandor Dargo

logo.pngC++’s undefined behaviour impacts safety. Sandor Dargo explains how and why uninitialised reads will become erroneous behaviour in C++26, rather than being undefined behaviour.

C++26: Erroneous Behaviour

by Sandor Dargo

From the article:

If you pick a random talk at a C++ conference these days, there is a fair chance that the speaker will mention safety at least a couple of times. It’s probably fine like that. The committee and the community must think about improving both the safety situation and the reputation of C++.

If you follow what’s going on in this space, you are probably aware that people have different perspectives on safety. I think almost everybody finds it important, but they would solve the problem in their own way.

A big source of issues is certain manifestations of undefined behaviour. It affects both the safety and the stability of software. I remember that a few years ago when I was working on some services which had to support a 10× growth, one of the important points was to eliminate undefined behaviour as much as possible. One main point for us was to remove uninitialized variables which often lead to crashing services.

Thanks to P2795R5 by Thomas Köppe, uninitialized reads won’t be undefined behaviour anymore – starting from C++26. Instead, they will get a new behaviour called ‘erroneous behaviour’.

The great advantage of erroneous behaviour is that it will work just by recompiling existing code. It will diagnose where you forgot to initialize variables. You don’t have to systematically go through your code and let’s say declare everything as auto to make sure that every variable has an initialized value. Which you probably wouldn’t do anyway.

Writing Senders -- Lucian Radu Teodorescu

logo.pngSenders/receivers can be used to introduce concurrency. Lucian Radu Teodorescu describes how to implement senders.

Writing Senders

by Lucian Radu Teodorescu

From the article:

If people are just using frameworks based on std::execution, they mainly need to care about senders and schedulers. These are user-facing concepts. However, if people want to implement sender-ready abstractions, they also need to consider receivers and operation states – these are implementer-side concepts. As this article mainly focuses on the implementation of sender abstractions, we need to discuss these two concepts in more detail.

A receiver is defined in P2300 as “a callback that supports more than one channel” [P2300R10]. The proposal defines a concept for a receiver, unsurprisingly called receiver. To model this concept, a type needs to meet the following conditions:

  • It must be movable and copyable.
  • It must have an inner type alias named receiver_concept that is equal to receiver_t (or a derived type).
  • std::execution::get_env() must be callable on an object of this type (to retrieve the environment of the receiver).

A receiver is the object that receives the sender’s completion signal, i.e., one of set_value()set_error(), or set_stopped(). As explained in the December 2024 issue [Teodorescu24], a sender may have different value completion types and different error completion types. For example, the same sender might sometimes complete with set_value(int, int), sometimes with set_value(double), sometimes with set_error(std::exception_ptr), sometimes with set_error(std::error_code), and sometimes with set_stopped(). This implies that a receiver must also be able to accept multiple types of completion signals.

C++26: constexpr Exceptions -- Sandor Dargo

In recent weeks, we’ve explored language features and library features becoming constexpr in C++26. Those articles weren’t exhaustive — I deliberately left out one major topic: exceptions.

SANDOR_DARGO_ROUND.JPGStarting with C++26, it will become possible to throw exceptions during constant evaluation. This capability is enabled through both language and library changes. Given the significance of this feature, it deserves its own dedicated post.

C++26: constexpr Exceptions

by Sandor Dargo

From the article:

P3068R6: Allowing exception throwing in constant-evaluation

The proposal for static reflection suggested allowing exceptions in constant-evaluated code, and P3068R6 brings that feature to life.

constexpr exceptions are conceptually similar to constexpr allocations. Just as a constexpr string can’t escape constant evaluation and reach runtime, constexpr exceptions also have to remain within compile-time code.

Previously, using throw in a constexpr context caused a compilation error. With C++26, such code can now compile — unless an exception is actually thrown and left uncaught, in which case a compile-time error is still issued. But the error now provides more meaningful diagnostics.

 

An option(al) to surprise you -- Andreas Fertig

me.pngIn today's post I share a learning of a customer with you. A while back, a customer asked me to join a debugging session. They had an issue they didn't (fully) understand.

An option(al) to surprise you

by Andreas Fertig

From the article:

The base

What I will show you is a much down-stripped and, of course, altered version. It was about a message system, but that's not important. Have a look at the code below.

2025-05-09_14-05-45.png

You can see a class enum for a state and a function Worker. The function takes a State and a const char* message named data. The function's job is to create a std::optional containing the user data, a C-style string.

 

Implementing a Struct of Arrays -- Barry Revzin

Data-oriented design is all about reorganizing data for better performance, and Andrew Kelley’s talk on the topic—especially his use of Zig’s MultiArrayList—offered a compelling real-world example. Inspired by that, this post explores how we can achieve a similar “struct-of-arrays” approach in C++26 using reflection to build a SoaVector<T> that separates member storage for improved memory locality and performance.

Implementing a Struct of Arrays

by Barry Revzin

From the article:

Recently, I watched Andrew Kelley’s talk on Practical Data Oriented Design. It goes into some of the architectural changes he’s been making to the Zig compiler, with pretty significant performance benefit. Would definitely recommend checking out the talk, even if you’re like me and have never written any Zig.

About halfway through the talk, he shows a way to improve his memory usage by avoiding wasting memory. By turning this structure:

const Monster = struct { 
    anim : *Animation, 
    kind : Kind, 

    const Kind = enum { 
    snake, bat, wolf, dingo, human 
    }; 
}; 

var monsters : ArrayList(Monster) = .{}; 

into this one:

var monsters : MultiArrayList(Monster) = .{}; 

ArrayList(Monster) is what we could call std::vector<Monster>, and MultiArrayList(Monster) now stores the anims and kinds in two separate arrays, instead of one. That is, a struct of arrays instead of an array of structs. But it’s a tiny code change.

C++20 Concepts for Nicer Compiler Errors -- Daniel Lemire

image-33-825x510.jpgTemplates are one of C++’s most powerful features, enabling developers to write generic, reusable code—but they come with a cost: notoriously verbose and opaque error messages. With the introduction of concepts in C++20, we can now impose clear constraints on template parameters and get far more helpful diagnostics when something goes wrong.

C++20 Concepts for Nicer Compiler Errors

by Daniel Lemire

From the article:

In C++, templates enable generic programming by allowing functions and classes to operate on different data types without sacrificing type safety. Defined using the template keyword, they let developers write reusable, type-agnostic code, such as functions (e.g., template <typename T> max(T a, T b)) or classes (e.g., std::vector), where the type T is specified at compile time.

Historically, the C++ language has tended to produce complicated compiler error messages. The main culprit is template metaprogramming. C++ templates are powerful but complex. When errors occur in template code, the compiler generates long, verbose messages with nested type information, often involving deep template instantiations. A simple mistake in a template function can produce a message spanning multiple lines with obscure type names.

Let us consider an example. In C++, we often use the ‘Standard Template Library (STL)’. It includes a useful dynamic array template: std::vector. A vector manages a sequence of elements with automatic memory handling and flexible sizing. Unlike fixed-size arrays, it can grow or shrink at runtime through operations like push_back to append elements or pop_back to remove them. You can store just about anything in an std::vector but there are some limits. For example, your type must be copyable.

C++26: More constexpr in the Standard Library -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGHere are the standard library features that will soon be usable at compile time. One topic is missing: exceptions. As they need both core language and library changes, I thought they deserved their own post.

C++26: More constexpr in the Standard Library

by Sandor Dargo

From the article:

P2562R1constexpr stable sorting

This paper proposes making std::stable_sortstd::stable_partitionstd::inplace_merge, and their ranges counterparts usable in constant expressions. While many algorithms have become constexpr over the years, this family related to stable sorting had remained exceptions — until now.

The recent introduction of constexpr containers gives extra motivation for this proposal. If you can construct a container at compile time, it’s only natural to want to sort it there, too. More importantly, a constexpr std::vector can now support efficient, stable sorting algorithms.

A key question is whether the algorithm can meet its computational complexity requirements under the constraints of constant evaluation. Fortunately, std::is_constant_evaluated() provides an escape hatch for implementations. For deeper details, check out the proposal itself.

Fixing exception safety in our task_sequencer -- Raymond Chen

RaymondChen_5in-150x150.jpgSome time ago, we developed a task_sequencer class for running asynchronous operations in sequence. There’s a problem with the implementation of Queue­Task­Async: What happens if an exception occurs?

Fixing Exception Safety in our task_sequencer

by Raymond Chen

From the article:

Let’s look at the various places an exception can occur in Queue­Task­Async.

    template<typename Maker>
    auto QueueTaskAsync(Maker&& maker) ->decltype(maker())
    {
        auto current = std::make_shared<chained_task>();
        auto previous = [&]
        {
            winrt::slim_lock_guard guard(m_mutex);
            return std::exchange(m_latest, current); ← oops
        }();

        suspender suspend;

        using Async = decltype(maker());
        auto task = [](auto&& current, auto&& makerParam,
                       auto&& contextParam, auto& suspend)
                    -> Async
        {
            completer completer{ std::move(current) };
            auto maker = std::move(makerParam);
            auto context = std::move(contextParam);

            co_await suspend;
            co_await context;
            co_return co_await maker();
        }(current, std::forward<Maker>(maker),
          winrt::apartment_context(), suspend);

        previous->continue_with(suspend.handle);

        return task;
    }

If an exception occurs at make_shared, then no harm is done because we haven’t done anything yet.

If an exception occurs when starting the lambda task, then we are in trouble. We have already linked the current onto m_latest, but we will never call continue_with(), so the chain of tasks stops making progress.

To fix this, we need to delay hooking up the current to the chain of chained_tasks until we are sure that we have a task to chain. 

How to Join or Concat Ranges, C++26 -- Bartlomiej Filipek

howtojoinconcat-filipek.pngC++ continues to refine its range library, offering developers more efficient and expressive ways to manipulate collections. In this post, we'll dive into three powerful range adaptors—concat_view, join_view, and join_with_view—exploring their differences, use cases, and practical examples.

How to Join or Concat Ranges, C++26

by Bartlomiej Filipek

From the article:

Modern C++ continuously improves its range library to provide more expressive, flexible, and efficient ways to manipulate collections. Traditionally, achieving tasks like concatenation and flattening required manual loops, copying, or custom algorithms. With C++’s range adaptors, we now have an elegant and efficient way to process collections lazily without unnecessary allocations.

In this post, we will explore three powerful range adaptors introduced in different C++ standards:

  • std::ranges::concat_view (C++26)
  • std::ranges::join_view (C++20)
  • std::ranges::join_with_view (C++23)

Let’s break down their differences, use cases, and examples.

std::ranges::concat_view (C++26)

The concat_view allows you to concatenate multiple independent ranges into a single sequence. Unlike join_view, it does not require a range of ranges—it simply merges the given ranges sequentially while preserving their structure.

In short:

  • Takes multiple independent ranges as arguments.
  • Supports random access if all underlying ranges support it.
  • Allows modification if underlying ranges are writable.
  • Lazy evaluation: No additional memory allocations.