Articles & Books

Constructing Containers from Ranges in C++23 -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGStarting from C++23, standard containers support a new set of constructor overloads. These constructors take a std::from_range tag, a range and an optional allocator. These from_range constructors make it easier to construct containers from ranges, helping make C++ code more concise, more expressive, and less error-prone.

Constructing Containers from Ranges in C++23

by Sandor Dargo

From the article:

I’ve written plenty on this blog about standard algorithms, but far less about ranges. That’s mostly because, although I’ve had production-ready compilers with C++20 ranges since late 2021, the original ranges library lacked a few key capabilities.

The biggest gap was at the end of a pipeline: you could transform data lazily, but you couldn’t drop the result straight into a brand-new container. What you got back was a view; turning that view into, say, a std::vector still required the old iterator-pair constructor.

C++23 fixes that in two complementary ways:

  • std::to (an adaptor that finishes a pipeline by converting to a container), and
  • from_range constructors on every standard container.

Today we’ll focus on the second improvement, because it’s the one you can implement in your own types, too.

The from_range constructor

Every standard container now supports a new set of constructors that make integration with ranges smoother — the so-called from_range constructors.

These constructors allow you to build a container directly from a range, rather than from a pair of iterators.

C++ Insights now uses Clang 20 -- Andreas Fertig

me.pngTime flies—C++ Insights just turned 7! To celebrate, I’ve upgraded the tool to Clang 20, unlocking even more C++23 and C++26 features for you to explore.

C++ Insights now uses Clang 20

by Andreas Fertig

From the article:

size_t

For a long time now, when you used size_t or std::size_t the resulting transformation kept the name. It did not expand to the underlying machine-specific date type. To be honest, that was more like a happy accident. Clang 20 came with two changes to libc++

The first https://github.com/llvm/llvm-project/commit/d6832a611a7c4ec36f08b1cfe9af850dad32da2e modularized <cstddef> for better internal structuring, avoiding too much content to be included. This patch was followed by a second one: https://github.com/llvm/llvm-project/commit/5acc4a3dc0e2145d2bfef47f1543bb291c2b866a. This one now made an interesting change.

Previously, libc++ defined std::size_t as

1
using ::size_t _LIBCPP_USING_IF_EXISTS; 

As the second patch highlighted, this required including the operating systems <stddef.h>. In the spirit of reducing unnecessary includes the line was changed to:

1
using size_t = decltype(sizeof(int)); 

This is an easy C++ solution to get the proper data type for size_t. Which is great. Yet, the AST nodes of the two versions look different. Previously, the operating system (macOS in this case) defined in its header:

1
typedef unsigned long size_t; 

Well, with the new version, the transformation no longer stops at size_t but expands it all down to unsigned long. This probably should have been the case from the beginning, but I liked that tests and transformations did not change across platforms in this specific case. However, there are other instances where the transformation did yield different output on different platforms, so I accept this one.

 

What’s New for C++ Developers in Visual Studio 2022 17.14 -- Sy Brand

RE1Mu3b.pngVisual Studio 2022 version 17.14 is now generally available! This post summarizes the new features you can find in this release for C++. You can download Visual Studio 2022 from the Visual Studio downloads page or upgrade your existing installation by following the Update Visual Studio Learn page.

What’s New for C++ Developers in Visual Studio 2022 17.14

by Sy Brand

From the article:

We’ve made a myriad of fixes and improvements to the MSVC compiler and standard library. See C++ Language Updates in MSVC in Visual Studio 2022 17.14 for a full list of changes on the compiler side, and the STL Changelog for all the standard library updates.

Compiler

We’ve added support for several C++23 features, which are available under the /std:c++latest and /std:c++23preview flags.

You can now omit () in some forms of lambdas that previously required them, thanks to P1102R2:

auto lambda = [] constexpr { }; //no '()' needed after the capture list
We implemented if consteval, with which you can run different code depending on whether the statement is executed at compile time or run time. This is useful for cases where your run time version can be heavily optimized with compiler intrinsics or inline assembly that are not available at compile time:
constexpr size_t strlen(char const* s) {
    if consteval {
        // if executed at compile time, use a constexpr-friendly algorithm
        for (const char *p = s; ; ++p) {
            if (*p == '\0') {
                return static_cast<std::size_t>(p - s);
            }
        }
    } else {
        // if executed at run time, use inline assembly
        __asm__("SSE 4.2 magic");
    }
}

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.

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++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.