Herb Sutter's Blog, page 2
January 2, 2025
My little New Year’s Week project (and maybe one for you?)
Here is my little New Year’s Week project: Trying to write a small library to enable compiler support for automatic raw union member access checking.
The problem, and what’s neededDuring 2024, I started thinking: What would it take to make union accesses type-checked? Obviously, the ideal is to change naked union types to something safe.(*) But because it will take time and effort for the world to adopt any solution that requires making source code changes, I wondered how much of the safety we might be able to get, at what overhead cost, just by recompiling existing code in a way that instruments ordinary union objects?
Note: I describe this in my C++26 Profiles proposal, P3081R0 section 3.7. The following experiment is trying to validate/invalidate the hypothesis that this can be done efficiently enough to warrant including in an ISO C++ opt-in type safety profile. Also, I’m sure this has been tried before; if you know of a recent (last 10 years?) similar attempt that measured its results, please share it in the comments.
What do we need? Obviously, an extra discriminator field to track the currently active member of each union object. But we can’t just add a discriminator field intrusively inside each union object, because that would change the size and layout of the object and completely break link/ABI compatibility. So we have to store it… extrinsically? … as-if in a global map…?
But that sounds stupid scary: global thread safety lions, data locality tigers, and even some branches bears, O my! Could such extrinsic storage and additional checking possibly be efficient enough?
My little experimentI didn’t know, so earlier this year I wrote some code to find out, and this week I cleaned it up and it’s now posted here:
https://github.com/hsutter/cppfront/tree/main/experimentalThe workhorse is extrinsic_storage, a fast and scalable lock-free data structure to nonintrusively store additional Data for each pointer key. It’s wait-free for nearly all operations (not just lock-free!), and I’ve never written memory_order_relaxed this often in my life. It’s designed to be cache- and prefetcher-friendly, such as using SOA to store keys separately so that default hash buckets contain 4 contiguous cache lines of keys.
If you’re looking for a little New Year’s experiment…If you’re looking for a little project over the next few days to start off the year, may I suggest one of these:
Little Project Suggestion #1: Find a bug or improvement in my little lock-free data structure! I’d be happy to learn how to make it better, fire away! Extra points for showing how to fix the bug or make it run better, such as in a PR or your cloned repo.
Little Project Suggestion #2: Minimally extend a C++ compiler (Clang and GCC are open source) as described below, so that every construction/access/destruction of a union type injects a call to my little library’s union_registry<>:: functions which will automatically flag type-unsafe accesses. If you try this, please let me know in the comments what happens when you use the modified compiler on some real world source! I’m curious whether you find true positive union violations in the union-violations.log file – of course it will also contain false positives, because real code does sometimes use unions to do type punning on purpose, but you should be able to eliminate batches of those at a time by their similar text in the log file.
To make #2 easier, here’s a simple API I’ve provided as union_registry<>, which wraps the above in a compiler-intgration-targeted API. I’ll paste the comment documentation here:
// For an object U of union type, when inject a call to this (zero-based alternative #s)// U is created initialized on_set_alternative(&U,0) = the first alternative# is active// U is created uninitialized on_set_alternative(&U,invalid)// U.A = xxx (alt #A is assigned to) on_set_alternative(&U,A)// U.A (alt #A is otherwise mentioned) on_get_alternative(&U,A)// U is destroyed / goes out of scope on_destroy(&U)//// That's it. Here's an example:// {// union Test { int a; double b; };// Test t = {42}; union_registry<>::on_set_alternative(&u,0);// std::cout << t.a; union_registry<>::on_get_alternative(&u,0);// t.b = 3.14159; union_registry<>::on_set_alternative(&u,1);// std::cout << t.b; union_registry<>::on_get_alternative(&u,1);// } union_registry<>::on_destroy(&u);//// For all unions with under 256 alternatives, use union_registry<>// For all unions with between 256 and 16k alternatives, use union_registry// If you find a union with >16k alternatives, email me the story and use union_registryRough initial microbenchmark performanceMy test environment:
CPU: 2.60 GHz i9-13900H (14 physical cores, 20 logical cores)OSes: Windows 11, running MSVC natively and GCC and Clang via Fedora in WSL2My test harness provided here:
14 test runs: Each successively uses { 1, 2, 4, 8, 16 32, 64, 1, 2, 4, 8, 16, 32, 64 } threadsEach run tests 1 million union objects, 10,000 at a time, 10 operations on each union; the test type is union Union { char alt0; int alt1; long double alt2; };Each run injects 1 deliberate “type error” failure to trigger detection, which results in a line of text written to union-violations.log that records the bad union access including the source line that committed it (so there’s a little file I/O here too)Totals:14 million union objects created/destroyed140 million union object accesses (10 per object, includes construct/set/get/destroy)On my machine, here is total the run-time overhead (“total checked” time using this checking, minus “total raw” time using only ordinary raw union access), for a typical run of the whole 140M unit accesses:
Compilertotal raw (ms)total checked (ms)total overhead (ms)NotesMSVC 19.40 -O2~190~1020~830-Ox checked was the same or very slightly slower; -Os checked was 3x slowerGCC 14 -O3~170~600-800~430-630-O2 overall was slightly slower; note higher variabilityClang 18 -O3~170~510~340-O2 checked was about 40% slowerDividing that by 140 million accesses, the per-access overhead is:
Compilertotal overhead (ns) / total accessesaverage overhead / access (ns)MSVC830M ns / 140M accesses5.9 ns / accessGCC (midpoint)530M ns / 140M accesses3.8 ns / accessClang340M ns / 140M accesses2.4 ns / accessFinally, recall we’re running on a 2.6 GHhz processor = 2.6 clock cycles per ns, so in CPU clock cycles the per-access overhead is:
Compileraverage overhead / access (cycles)MSVC15 cyclesGCC9.9 cyclesClang6.2 cyclesThis… seems too good to be true. I may well be making a silly error (or several) but I’ll post anyway so we can all have fun correcting them! Maybe there’s a silly bug in my code, or I moved a decimal point, or I converted units wrong, but I invite everyone to have fun pointing out the flaw(s) in my New Year’s Day code and/or math – please fire away in the comments.
Elaborating on why this seems too good to be true: Recall that one “access” means to check the global hash table to create/find/destroy the union object’s discriminator tag (using std::atomics liberally) and then also set or check either the tag (if setting or using one of the union’s members) and/or the key (if constructing or destroying the union object). But even a single L2 cache access is usually around 10-14 cycles! This would mean this microbenchmark is hitting L1 cache almost always, even while iterating over 10,000 active unions at a time, often with more hot threads than there are physical or logical cores pounding on the same global data structure, and occasionally doing a little file I/O to report violations.
Even if I didn’t make any coding/calculation errors, one explanation is that this microbenchmark has great L1 cache locality because the program isn’t doing any other work, and in a real whole program it won’t get to run hot in L1 that often – that’s a valid possibility and concern, and that’s exactly why I’m suggesting Little Project #2, above, if anyone would like to give that little project a try.
In any event, thank you all for all your interest and support for C++ and its evolution and standardization, and I wish all of you and your families a happier and more peaceful 2025!
(*) Today we have std::variant which safely throws an exception if you access the wrong alternative, but variant isn’t as easy to use as union today, and not as type-safe in some ways. For example, the variant members are anonymous so you have to access them by index or by type; and every variant in the program is also anonymous == the same type, so we can’t distinguish/overload unrelated variants that happen to have similar alternatives. I think the ideal answer – and it looks like ISO C++ is just 1-2 years from being powerful enough to do this! – will be something like the safe union metaclass using reflection that I’ve implemented in cppfront, which is as easy to use as union and as safe as variant – see my CppCon 2023 keynote starting at 39:16 for a 4-minute discussion of union vs. variant vs a safe union metafunction that uses reflection.
December 11, 2024
My code::dive talk video is available: New Q&A
Two weeks ago, Bjarne and I and lots of ISO committee members had a blast at the code::dive C++ conference held on November 25, just two days after the end of the Wrocław ISO C++ meeting. Thanks again to Nokia for hosting the ISO meeting, and for inviting us all to speak at their conference! My talk was an updated-and-shortened version of my CppCon keynote (which I also gave at Meeting C++; I’ll post a link to that video too once it’s posted):
If you already saw the CppCon talk, you can skip to these “new parts at the end” where the Q&A got into very interesting topics:
44:48 Summary: Why 2024 has turned into a pivotal year for C++ (* transcript at bottom of this post)47:00 Erroneous behavior in C++: What’s the run-time cost and can we opt out when needed?48:00 My new paper proposing taking safety-related undefined behavior and turning it all off by default: I have a dream51:00 This new ^^ reflection operator: Will it litter our code?54:14 What do you think of evolving/extending C++ vs. directions like cppfront vs. C++ alternatives like Carbon?Finally, I’m glad I got a chance to give this last answer to cap things off, and thanks again for the audience question that led to it:
57:55 Why I think the most impactful way I can contribute toward improving our society is through improving existing C++ code (** transcript at bottom of this post)That morning, on our route while traveling from the hotel to the conference site, at one point we noticed that up ahead there was a long line of people all down the length of a block and wrapped around the corner. It took me a few beats to realize that was where we were going, and those were the people still waiting to get in to the conference (at that time there were already over 1,000 people inside the building). Here’s one photo that appeared in the local news showing part of the queue:

In all, I’m told 1,800 people attended on-site, and 8,000 attended online. Thank you again to our Nokia hosts for hosting the ISO C++ meeting and inviting us to code::dive, and thank you to all the C++ developers (and, I’m sure, a few C++-curious) who came from Poland and beyond to spend a day together talking about our favorite programming language!
(*) Here’s a transcript of what I said in that closing summary:
… Reflection and safety improvements as what I see are the two big drivers of our next decade of C++.
So I’m excited about C++. I really think that this was a turning point year, because we’ve been talking about safety for a decade, the Core Guidelines are a decade old, we’ve been talking about reflection for 20 years in the C++ committee — but this is the year that it’s starting to get real. This is the year we put erroneous behavior [in] and eliminated uninitialized locals in the standard, this is the year that we design-approved reflection for the standard — both for C++26 and hopefully they’ll both get in. We are starting to finally see these proposals land, and this is going to create a beautiful new decade, open up a new fresh era of C++. Bjarne [….] when C++11 came out, he said, you know, there’s been so many usability improvements here that C++11, even though it’s fully compatible with C++98, it feels like a new language. I think we’re about to do that again, and to make C++26 feel like a new language. And then just as we built on C++11 and finished it with C++14, 17, 20, the same thing with this generation. That’s how I view it. I’m very hopeful for a bright future for C++. Our language and our community continues to grow, and it’s great to see us addressing the problems we most need to address, so we have an answer for safety, we have an answer for simpler build systems and reducing the number of side languages to make C++ work in practice. And I’m looking forward to the ride for the next decade and more.
And at the end of the Q&A, the final part of my answer about why I’m focused on C++ rather than other efforts:
Why am I spending all this time in ISO C++? Not just because I’m some C++-lover on a fanatical level — you may accuse me of that too — but it’s just because I want to have an impact. I’m a user of this world’s society and civilization. I use this world’s banking system. I rely on this world’s hospital system. I rely on this world’s power grid. And darnit I don’t want that compromised, I want to harden it against attack. And if I put all my energy into some new programming language, I will have some impact, but it’s going to be much smaller because I can only write so much new code. If I can find a way to just recompile — that’s why you keep hearing me say that — to just recompile the billions of lines of C++ code that exist today, and make them even 10% safer, and I hope to make them much more than that safer, I will have had an outsized effect on securing our civilization. And I don’t mean to speak too grandiosely, but look at all the C++ code that needs fixing. If you can find a way to do that, it will have an outsized impact and benefit to society. And that’s why I think it’s important, because C++ is important — and not leaving all that code behind, helping that code too as well as new code, I think is super important, and that’s kind of my motivation.
November 24, 2024
Trip report: November 2024 ISO C++ standards meeting (Wrocław, Poland)
On Saturday, the ISO C++ committee completed the third-last design meeting of C++26, held in Wrocław, Poland. There are just two meetings left before the C++26 feature freeze in June 2025, and C++26 is on track to be completed two more meetings after that in early 2026. Implementations are closely tracking draft C++26; GCC and Clang already support about two-thirds of C++26 features right now.
Our host, Nokia, arranged for high-quality facilities for our six-day meeting from Monday through Saturday. We had over 220 attendees, about two-thirds in-person and the others remote via Zoom, formally representing 31 nations. At each meeting we regularly have new attendees who have never attended before, and this time there were 37 new first-time attendees, mostly in-person. To all of them, once again welcome!
The committee currently has 23 active subgroups, 15 of which met in parallel tracks throughout the week. Some groups ran all week, and others ran for a few days or a part of a day, depending on their workloads. We also had two combined informational evening sessions to inform the committee broadly about progress on key topics: one on contracts, the other on relocatability. You can find a brief summary of ISO procedures here.
This time, the committee adopted the next set of features for C++26, and made significant progress on other features that are now expected to be complete in time for C+26.
In addition to features already approved for C++26 at previous meetings, at this meeting three major features made strong progress:
P2996 Reflection is in specification wording review aiming for C++26.P2900 Contracts continues to have a chance of being in C++26 – probably more time was spent on contracts this week in various subgroups than on any other feature.P3081 Safety Profiles and P3471R0 Standard library hardening made progress into the Evolution group and have a chance of being an initial set of profiles for C++26.Adopted for C++26: Core language changes/featuresNote: These links are to the most recent public version of each paper. If a paper was tweaked at the meeting before being approved, the link tracks and will automatically find the updated version as soon as it’s uploaded to the public site.
In addition to fixing a list of defect reports, the core language adopted 8 papers, including the following…
P2686R5 “constexpr structured bindings and references to constexpr variables” by Corentin Jabot and Brian Bi. The paper itself contains a great explanation, pasting from page 8:
You can now declare structured bindings constexpr. Because structured bindings behave like references, constexpr structured bindings are subject to similar restrictions as constexpr references, and supporting this feature required relaxing the previous rule that a constexpr reference must bind to a variable with static storage duration. Now, constexpr references and structured bindings may also bind to a variable with automatic storage duration, but only when that variable has an address that is constant relative to the stack frame in which the reference or structured binding lives.
Thanks, Corentin and Brian!
P3068R6 “Allowing exception throwing in constant evaluation” by Hana Dusíková continues the very-welcome and quite-inexorable march toward allowing more and more of C++ to run at compile time. “Compile-time C++, now with exceptions so you can use normal C++ without rewriting your error handling to return codes/std::expecteds!” is the nutshell synopsis. Thank you, Hana!
Adopted for C++26: Standard library changes/featuresIn addition to fixing a list of defect reports, the standard library adopted 19 papers, including the following…
P3370R1 “Add new library headers from C23” by Jens Maurer is an example of how C++ continues trying to align with C. As the paper states: “C23 added the and headers. This paper [adds] these headers to C++ to increase the subset of code that compiles with C and C++. […] Type-generic macros and type-generic functions do not exist in C++, but function templates can provide the same call interface. Thus, the use of the former in C is replaced by the latter in C++.” Thank you Jens!
(For those who don’t know of Jens, he’s one of the committee’s unsung Energizer Bunnies: He chairs the Core language specification working group all week long at every meeting, he’s the key logistics person who makes every meeting run smoothly from organizing room layouts and A/V to break drinks/snacks, he organizes every meeting’s space allocations for all the subgroups and evening sessions, and after all that he clearly still has time left over to write standard library papers too! We don’t know how he does it all, but the unconfirmed rumor is that there’s a secret clone involved; investigation is still pending.)
Because Hana (already mentioned above) can’t stop adding to constexpr, P3309R3 “constexpr atomic and atomic_ref” by Hana Dusíková does what it says on the tin. If you’re wondering whether this is important (after all, we don’t support threads in constexpr code… yet… and usually atomic is about concurrency), the paper explains:
This paper […] allows implementing other types (std::shared_ptr, persistent data structures with atomic pointers) and algorithms (thread safe data-processing, like scanning data with atomic counter) with just sprinkling constexpr to their specification.
So perhaps a good synopsis would be: “removing the last excuse not to make shared_ptr available in constexpr code!” Thanks, Hana!
P1928R15 “std::simd — merge data-parallel types from the Parallelism TS 2” by Matthias Kretz adopts the data-parallel basic_simd types from the TS into C++26 as std::basic_simd. Two notes: (1) The “changes since the TS” section has a “constexpr everything” section, because that’s just how we roll in WG21 these days (cf: Hana’s papers above). (2) Note the “R” revision number, which indicates the 15 revisions of this proposal that were needed to get it to land — thank you for the determined hard work, Matthias, and once again congratulations from us all: When this proposal was adopted, a sustained round of loud applause filled the room!
Last but not least, P3325R5 “A utility for creating execution environments” by Eric Niebler builds on the huge new std::execution concurrency and parallelism library that was adopted at our previous meeting this summer (see my summer 2024 trip report) to additionally make it easier to create and merge loci of execution. Definitely read section 4 of the paper for a full description of the motivation and use cases. Thanks very much, Eric!
Other progressAll subgroups continued progress, more of which will no doubt be covered in other trip reports. Here are a few more highlights…
SG1 (Concurrency): Concurrent queues “might” make C++26, and this is one of the most compelling demonstrations of the “sender/receiver” design pattern in the new std::execution that was adopted at our previous meeting. Concurrent queues would also be (finally) the first concurrent data structure in the standard. Also, although SG1 has been trying to fix/specify memory_order_consume since C++11, this has not succeeded, so the feature has now been removed.
SG7 (Compile-Time Programming): Made progress on several papers. Perhaps the most visible decision was to decide on a reflection syntax, ^^, which apparently some people are lobbying hard to call the “unibrow operator.”
SG15 (Tooling): Progressed work on the Ecosystem IS (International Standard), which achieved the milestone of being approved to be forwarded to the main design subgroups.
SG20 (Education): Progressed work on creating C++ teaching guidelines about topics that should be taught in C++ educational settings. Encouraged paper authors and chairs to send papers to SG20 for teachability feedback.
SG21 (Contracts): Spent Wednesday in the Core working group (instead of meeting separately) because EWG sent contracts to Core aiming for C++26, so the contracts experts went to Core to help with wording review. Continued work on trying to increase consensus on some design details.
SG23 (Safety and Security): Approved several papers to progress, including to reduce undefined behavior in the language and specifically time travel optimizations, and to send an initial set of core safety profiles to EWG aiming for C++26. The proposal P3390 “Safe C++” by Sean Baxter was seen and received support as a direction that can be pursued in addition to and complementary with Profiles, where Profiles are useful to define subsets of current C++ and its features and reducing its undefined behaviors to reduce unsafety in current C++, and proposals like Safe C++ can be useful to propose new extensions for an expanded C++ to try to achieve larger/stronger safety guarantees not possible in just a subset/constrained C++. The SG23 vote on which to prioritize was 19:11:6:9 for Profiles:Both:Neutral:SafeC++.
EWG (Language Evolution Working Group) forwarded contracts aiming for C++26, understanding however that there are still a few unresolved contentious design points; the groups are working on increasing consensus between now and the next meeting. Adopted the ^^ operator for reflection. Pattern matching is still trying to make C++26. Three safety papers progressed: P3081 core safety profiles (see below; for detailed telecon review between meetings and aiming for approval at our next meeting), P3471R0 “Standard library hardening” by Konstantin Varlamov and Louis Dionne which passed unanimously (no one even neutral!), and P2719 “Type-aware allocation and deallocation functions” by Louis Dionne and Oliver Hunt which offers safety mitigation building blocks. The group also approved trivial relocatability for C++26.
LEWG (Library Evolution Working Group) reviewed 30 papers this week. LEWG is specifically prioritizing its time on these topics as papers are available: relocatability, parallel algorithms, concurrent queues, constexpr containers, safety profiles, and pattern matching.
Thank you to all the experts who worked all week in all the subgroups to achieve so much this week!
What’s nextOur next meeting will be in Hagenberg, Austria hosted by University of Applied Sciences Upper Austria, RISC Software GmbH, and Softwarepark Hagenberg.
Thank you again to the over 220 experts who attended on-site and on-line at this week’s meeting, and the many more who participate in standardization through their national bodies!
C++ is a living language and moving fast. Thank you again to everyone reading this for your interest and support for C++ and its standardization.
Coda: My papers at this meetingMy papers aren’t what’s most important, but since this is my trip report I should mention my papers.
I had eight papers at this meeting, of which seven were seen at the meeting and one will be seen in an upcoming telecon. Here they are in the rough order they were seen…
P3437R0 “Principles: Reflection and generation of source code”On Monday, in the SG7 (Compile-Time Programming) subgroup I presented P3437R0 “Principles: Reflection and generation of source code.”
In a nutshell, P3437 advocates for the principle that reflection and generation are primarily for automating reading and writing source code, the way a human programmer would do but so the human doesn’t have to do it by hand. I argue that this view implies that, at least by default:
reflecting a class (or other entity) in consteval compile-time code should be able to see everything a human could know from reading the source code of the class’s definition; and, similarly, generating a class in consteval code should be able to write anything the human programmer could write (including things like adding specializations to std) and have the same language meaning as if the programmer wrote it by hand (including normal name lookup and access control).Non-default modes might do more or different things, but I argued that “as if the human read/wrote the source code” should be the default semantics and get the “nice” syntax that C++ programmers would naturally expect unless they were doing something special.
The group agreed and approved the paper, 15:8:3 in favor:neutral:against.
P3439 “Chained comparisons: Safe, correct, efficient”On Tuesday, I presented P3439 “Chained comparisons: Safe, correct, efficient” in the language evolution working group (EWG). This paper proposes that comparison chains like a <= b < c actually work. (I’ve already implemented this in cppfront.) Today, people try to write such code, and the standard says it’s required to compile but do the wrong thing, which is not great, and all major compilers will warn about it but then compile it anyway because they must. Adopting this proposal would fix real bugs, including security bugs, just by recompiling existing code. This change would also make the language simpler, because with this feature we could write a <= b && b < c as just a <= b < c. And that would also make the language slightly faster, by naturally avoiding multiple evaluation of middle terms like b.
This is the only part of my original paper P0515R0 “Consistent comparisons” (aka the operator spaceship <=> paper) that has not yet been adopted into the standard. It was rejected before the pandemic when Barry Revzin and I tried a second time to get it adopted, but I felt it was appropriate to bring it back now because there’s new urgency and new information. A few highlights from the paper:
Today, comparison chains like min <= index_expression < max are valid code that do the wrong thing; for example, 0 <= 100 < 10 means true < 10 which means true, certainly a bug. Yet that is exactly a natural bounds check; for indexes, such subscript chains’ current meaning is always a potentially exploitable out-of-bounds violation.
[P0893R1] reported that code searches performed by Barry Revzin with Nicolas Lesser and Titus Winters found:
Lots of instances of such bugs in the wild: in real-world code “of the assert(0 <= ratio <= 1.0); variety,” and “in questions on StackOverflow [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11].”“A few thousand instances over just a few code bases” (emphasis original) where programmers today write more-brittle and less-efficient long forms such as min <= index_expression && index_expression < max because they must, but with multiple evaluation of index_expression and with bugs because of having to write the boilerplate and sometimes getting it wrong.
See the paper for more new information that warrants considering this anew.
The group voted 30:2:2 in favor:neutral:against for me to bring this back with standardese wording to the February meeting, and if things go well there it might make C++26.
Next: Three safety/security proposals for me at this meetingAlthough the chained-comparisons proposal had a safety and security aspect, I don’t consider it principally a “safety proposal.” However, I did present three actual safety and security proposals at this meeting, and here they are… (Note: There were other such proposals by others too, not just by me! And I’m excited about them too. These are just my three…)
Of the four main language safety categories we have to improve (type, bounds, initialization, and lifetime), the first three are easier and are the focus of P3436 and P3081. Lifetime is the hardest to improve in C++, and requires more effort, in my proposal’s case writing a static analysis rule engine; but I think major improvement is possible without heavy annotation of existing code, and this is the subject of P3465 (promoting P1179).
Safety #1 of 3: P3436R0 “Removing safety-related undefined behavior by default”On Wednesday morning in SG23 (Safety and Security), I presented P3436R0 “Removing safety-related undefined behavior (UB) by default.” This would be a pretty big deal… as I mentioned in the presentation, I firmly believe that most observers of C++, even friendly ones, do not expect us to be able to “do something significant” about UB in C++. UB is after all a fundamental part of C++, right? … Right?
My conjecture is “no it isn’t, and the point of this paper is easy to summarize:
C++ already bans undefined behavior in a huge subset of the language: constexpr evaluation. A lot of people do not realize that, yes, we already did that… we already snuck a lot of safety into the language while no one was paying particular attention. Those who say UB is endemic throughout C++ have already started to fall behind the times, since we added and then gradually expanded constexpr.Let’s enumerate each such case of UB already prevented in constexpr evaluation, and prevent it in normal execution too: either always (if it’s cheap and easy, as we just did with uninitialized local variables), or else under a Profile (so any overhead has an easy way to opt-in/opt-out when you do/don’t want the safety).I think most people don’t expect that C++ could make a major dent in undefined behavior and still be true to C++. But P3436 makes the bold conjecture that maybe we can not only make a dent, but actually eliminate UB in C++ by default when safety Profiles are enabled, by combining (a) that we already do it in constexpr code, with (b) that we plan to have Profiles as a tool to opt in/out of safety modes that require source changes or execution overheads.
The group agreed and voted unanimously 25:3:0 in favor:neutral:against.
Important reality check here: This is the start, not the end… this doesn’t mean the proposal is done, it means the group said “we like the idea, now go do the hard work of actually listing all the UB cases and how you propose to handle each one and writing up standard specification wording for all that.” Most “encouragement” polls in the committee actually mean “yes please go do more work.” (This takes a lot of getting used to for new participants.)
Safety #2 of 3: P3465R0 “Pursue P1179 as a Lifetime TS”On Wednesday after lunch in SG23 (Safety and Security), I made the first-ever presentation in WG21 of the C++ Core Guidelines Lifetime safety static analysis profile, of which I’m the primary designer (with lots of expert help; big thanks again everyone listed in P1179!). This analysis catches many common lifetime dangling issues (not just for pointers, but for generalized Pointers including iterators and views), usually with little or no source annotation. This is a full portable “static analysis specification” which means a detailed spec of state and state transitions that an implementation should do in a function body, so that different implementations will give the same answers.
I first publicly presented this work, with live demos of the first prototype, in my CppCon 2015 talk (starting at 30:28). I then published the detailed specification on GitHub and cc’d/FYI’d ISO via the paper P1179 “Lifetime safety: Preventing common dangling” back in 2019, but at that point I was just giving the committee an FYI… I never asked to present it for adoption, in part because I wanted to gain more usage experience, and in part because at the time the committee did not yet have this kind of safety as a first-order priority.
Now, times have changed: Parts of this have now been implemented and shipped in Microsoft Visual Studio, JetBrains CLion, a Clang fork, and even a smidgen in Clang trunk I’m told. And safety/security is finally a first-order concern, so the current paper P3465R0 “Pursue P1179 as a Lifetime TS” points back to P1179 and suggests that the time is ripe to turn it into an ISO C++ Technical Specification. I proposed that we pursue turning this analysis specification into a Technical Specification (TS) separate from the standard in order to get one more round of WG21-endorsed experience before we cast it in stone as a lifetime Profile: While implementations have implemented a lot of the design, they haven’t yet implemented all of it, and I think making it a TS would show WG21 interest and spur them to complete their implementations so that we could validate the last few important parts too on large amounts of real-world code, and make any needed adjustments. If all goes well with that, I hope to propose it for the standard itself in the future.
The group voted 24:0:1 in favor:neutral:against to direct me to turn P1179 into a working paper for a Technical Specification. Fortunately, the specification of state and state transitions is already very concrete, so it’s already at approximately the same level of detail as normal C++ language specification wording.
Safety #3 of 3: P3081R0 “Core safety Profiles: Specification, adoptability, and impact”My third safety/security paper was P3081R0 “Core safety Profiles: Specification, adoptability, and impact.” I presented it Wednesday afternoon in SG15 (Tooling), Thursday morning in SG23 (Safety and Security), and Friday morning in EWG (the main language evolution group), with a target of C++26.
This is a companion paper to my “C++ safety, in context” blog essay this spring; see that essay for the full motivation, context, and rationale. P3081 contains the concrete proposed semantics:
Proposes a concrete initial set of urgently needed enforced safety Profiles.Described how a Profiles implementation can prioritize adoptability and safety improvement impact, especially to silently fix serious bugs in existing code by just recompiling the code (i.e., no manual code changes required) where possible, and making it easy to opt into broadly applicable safety profiles.I tried to push the boundaries of what’s possible in C++ by suggesting we do something we’ve never done before in the ISO C++ standard: Have the Standard actually normatively (i.e., as a hard requirement) require implementations (usually compilers, and not third-party post-build tools) to offer safety-related “fixits” to automatically correct C++ code where the fix can be super reliable. We’ve never formally required anything like this before, but I felt it was important to try to push this boundary to raise the bar for all compilers because it’s so important for safety adoptability… yet I really wasn’t sure how this suggestion would fly (or get shot out of the sky, as boundary-pushing proposals tend to be).
The first presentation was on Wednesday after lunch to SG15 (Tooling) because the paper suggested this novelty of requiring C++ compilers to offer automatic fixits. The votes on two related polls were both unanimously 8:2:0 in favor:neutral:against. People in the room included tooling and compiler experts for GCC and Clang, and their main comment was (slightly paraphrased) ‘yeah, sure, this is 2024, our C++ compilers all already offer fixits, let’s require compilers to do it consistently.’ Whew.
The second presentation was on Thursday after breakfast to SG23 (Safety and Security), which has been encouraging work on Profiles but has not yet had any concrete proposal to forward to EWG, the main evolution working group. SG23 gave feedback, and then voted unanimously 23:1:0 in favor:neutral:against to forward P3081 to EWG specifically targeting C++26, including that Bjarne’s paper and this one should be merged to reflect the syntax decisions made earlier in the day based on Bjarne’s paper. This is the first Profiles proposal to be forwarded from SG23.
The third presentation was on Friday after breakfast to EWG, which voted 44:4:3 in favor:neutral:against to pursue this paper for C++26, and schedule teleconferences between now and February to go line-by-line through the paper in detail.
Disclaimer: Note this means we have to do a ton of work in the next few months, if it is to have a hope of actually making C++26.
P2392R3 “Pattern matching using is and as”On Thursday in EWG, I presented P2392R3 “Pattern matching using is and as.” There are two main parts to this paper: is/as expressions to unify and simplify safe queries/casts, and inspect pattern matching that uses the is/as approach.
This time the results were quite mixed, with no consensus to encourage proceeding with my proposal: For the whole paper, EWG voted 19:6:22 in favor:neutral:against. For just the is/as portion, EWG voted 21:8:20 in favor:neutral:against. Clearly no consensus at all, never mind not strong encouragement, and the competing proposal from Michael Park got 33:6:10. But I’m not giving up P2392… I’ll try to incorporate the feedback heard in the room, and perhaps improve consensus at the next meeting or two.
P3466R0 “(Re)affirm design principles for future C++ evolution”Finally, on Friday afternoon in EWG, I presented P3466 “(Re)affirm design principles for future C++ evolution.” Note: I presented the initial revision R0, and this link is to a draft revision R1 that incorporates the direction from the group (but my R1 edits are still being reviewed to make sure I applied EWG’s direction correctly).
The summary up front is:
C++ is a living language that continues to evolve. Especially with C++26 and compile-time programming, and new proposals for type and memory safety, we want to make sure C++ evolution remains as cohesive and consistent as possible so that (a) it’s “still C++” in that it hews to C++’s core principles, and (b) it’s delivering the highest quality value to make C++ code safer and simpler.
The Library Evolution WG has adopted written design principles to guide proposals and their discussion. This paper proposes that the Language Evolution WG also adopt written principles to guide new proposals and their discussion, and proposes the principles in this paper as a starting point.
EWG voted to turn this paper into a new Standing Document (which future papers can add to) to document EWG’s values: 29:22:2 in favor:neutral:against.
P0707R5 “Metaclass functions for generative C++”The committee didn’t have time to consider all papers at the meeting, and my paper P0707R5 “Metaclass functions for generative C++” is one that got deferred to be considered at a between-meetings Zoom telecon over the winter.
That’s it for my papers this time… whew. More news next time, from Austria…
November 11, 2024
A new chapter, and thoughts on a pivotal year for C++
Starting today I’m excited to be working on a new team, with my C++ standards and community roles unchanged. I also wanted to write a few words about why I’m excited about continuing to invest my time heavily in C++’s standardization and evolution especially now, because I think 2024 has been a pivotal year for C++ — and so this has turned into a bit of an early “year-end C++ retrospective” post too.
It’s been a blast to be on the Microsoft Visual C++ compiler team for over 22 years! The time has flown by because the people and the challenges have always been world-class. An underappreciated benefit of being on a team that owns a foundational technology (like a major C++ compiler) is that you often don’t have to change teams to find interesting projects, because new interesting projects need compiler support and so tend to come to you. That’s been a real privilege, and why I stuck around way longer than any other job I’ve held. Now I am finally going to switch to a new job, but I’ll continue to cheer my colleagues on as a happy MSVC user on my own projects, consuming all the cool things they’re going to do next!
Today I’m thrilled to start at Citadel Securities, a firm that “combines deep trading acumen with leading-edge analytics and technology to deliver liquidity to some of the world’s most important markets, retail brokerages, and financial institutions.” I’ve known folks at CitSec for many years now (including some who participate in WG 21) and have long known it to be a great organization with some of the brightest minds in engineering and beyond. Now I’m looking forward to helping to drive CitSec’s internal C++ training initiatives, advise on technical strategy, share things I’ve learned along the way about sound design for both usability and pragmatic adoptability, and mentor a new set of talented folks there to not only take their own skilled next steps but also to themselves become mentors to others in turn. I think a continuous growth and learning culture like I’ve seen at CitSec consistently for over a dozen years is one of the most important qualities a company can have, because if you have that you can always grow all the other things you need, including as demands evolve over time. But maybe most of all I’m looking forward to learning a lot myself as I dive back into the world of finance — finance is where I started my junior career in the 80s and 90s, and I’m sure I’ll learn a ton in CitSec’s diverse set of 21st-century businesses that encounter interesting, leading-edge technical challenges every day that go well beyond the ones I encountered back in the 20th.
My other C++ community roles are unchanged — I’m continuing my current term as chair of the ISO C++ committee, I’m continuing as chair of the Standard C++ Foundation, and especially I’m continuing to work heavily on ISO C++ evolution (I have eight papers in the current mailing for this month’s Wrocław meeting!) including supporting those with cppfront prototype implementations. I meant it when I said in my CppCon talk that C++’s next decade will be dominated by reflection and safety improvements, and that C++26 really is shaping up to be the most impactful release since C++11 that started a new era of C++; it’s an exciting time for C++ and I plan to keep spending a lot of time contributing to C++26 and beyond.
Drilling down a little: Why is 2024 a pivotal year for C++? Because for the first time in 2024 the ISO committee has started adopting (or is on track to soon adopt) serious safety and reflection improvements into the draft C++ standard, and that’s a big turning point:
For safety: With uninitialized local variables no longer being undefined behavior (UB) in C++26 as of March 2024, C++ is taking a first serious step to really removing safety-related UB, and achieve the ‘holy grail’ of an easy adoption story: “Just recompile your existing code with a C++26 compiler, with zero manual code changes, and it’s safer with less UB.” This month, I’m following up on that proposing P3436R1, a strategy for how we could remove all safety-related UB by default from C++ — something that I’m pretty sure a lot of folks can’t imagine C++ could ever do while still remaining true to what makes C++ be C++, but that in fact C++ has already been doing for years in constexpr code! The idea I’m proposing is to remove the same cases of UB we already do in constexpr code also at execution time, in one of two ways for each case: when it’s efficient enough, eliminate that case universally the same as we just did for uninitialized locals; otherwise, leverage the great ideas in the Profiles proposals as a way to opt in/out of that case (see P3436 for details). If the committee likes the idea enough to encourage me to go do more work to flesh it out, over the winter I’ll invest the time to expand the paper into a complete catalog of safety-related UB with a per-case proposal to eliminate that UB at execution time. If we can really achieve a future C++ where you can “just recompile your existing code with safety Profiles enabled, and it’s safer with zero safety-related UB,” that would be a huge step forward. (Of course, some Profiles rules will require code changes to get the full safety benefits; see the details in section 2 of my supporting Profiles paper.)For reflection: Starting with P2996R7 whose language part was design-approved for C++26 in June 2024, we can lay a foundation to then build on with follow-on papers like P3294R2 and P3437R1 to add generation and more features. As I demonstrated with examples in the above-linked CppCon talk, reflection (including generation) will be a game-changer that I believe will dominate the next decade of C++ as we build it out in the standard and learn to use it in the global C++ community. I’m working with P2996/P3294 prototypes and my own cppfront compiler to help gather usability experience, and I’m contributing my papers like P0707R5 and P3437R1 as companion/supporting papers to those core proposals to try to help them progress.As Bjarne Stroustrup famously said, “C++11 [felt] like a new language,” starting a new “modern” C++ style featuring auto and lambdas and standard safe smart pointers and range-for and move semantics and constexpr compile-time code, that we completed and built on over the next decade with C++14/17/20/23. (And don’t forget that C++11’s move semantics already delivered the ideal adoption story of “just recompile your existing code with a C++11 compiler, with zero manual code changes, and it’s faster.”) Since 2011 until now, “modern C++” has pretty much meant “C++ since C++11” because C++11 made that much of a difference in how C++ worked and felt.
Now I think C++26 is setting the stage to do that again for a second time: Our next major era of what “modern C++” will mean will be characterized by having safety by default and first-class support for reflection-based generative compile-time libraries. Needless to say, this is a group effort that is accomplished only by an amazing set of C++ pros from dozens of countries, including the authors of the above papers but also many hundreds of other experts who help design and review features. To all of those experts: Again, thank you! I’ll keep trying to contribute what I can too, to help ship C++26 with its “version 1” of a set of these major new foundational tools and to continue to add to that foundation further in the coming years as we all learn to use the new features to make our code safer and simpler.
C++ is critically important to our society, and is right now actively flourishing. C++ is essential not only at Citadel Securities itself, but throughout capital markets and the financial industry… and even that is itself just one of the critical sectors of our civilization that heavily depend on C++ code and will for the foreseeable future. I’m thrilled that CitSec’s leadership shares my view of that, and my same goals for continuing to evolve ISO C++ to make it better, especially when it comes to increasing safety and usability to harden our society’s key infrastructure (including our markets) and to make C++ even easier to use and more expressive. I’m excited to see what the coming decade of C++ brings… 2024 really has shaped up to be a pivotal year for C++ evolution, and I can’t wait to see where the ride takes us next.
October 23, 2024
Podcast interview: Rust and C++
In early September I had a very enjoyable technical chat with Steve Klabnik of Rust fame and interviewer Kevin Ball of Software Engineering Daily, and the podcast is now available.
Disclaimer: Please just ignore the “vs” part of the “Rust vs C++” title. The rest of the page is a much more accurate description of a really fun discussion I’d be happy to do again anytime!
Here’s the info…
Rust [and] C++ with Steve Klabnik and Herb Sutter![]()
In software engineering, C++ is often used in areas where low-level system access and high-performance are critical, such as operating systems, game engines, and embedded systems. Its long-standing presence and compatibility with legacy code make it a go-to language for maintaining and extending older projects. Rust, while newer, is gaining traction in roles that demand safety and concurrency, particularly in systems programming.
We wanted to explore these two languages side-by-side, so we invited Herb Sutter and Steve Klabnik to join host Kevin Ball on the show. Herb works at Microsoft and chairs the ISO C++ standards committee. Steve works at Oxide Computer Company, is an alumnus of the Rust Core Team, and is the primary author of The Rust Programming Language book.
We hope you enjoy this deep dive into Rust and C++ on Software Engineering Daily.
![]()
Kevin Ball or KBall, is the vice president of engineering at Mento and an independent coach for engineers and engineering leaders. He co-founded and served as CTO for two companies, founded the San Diego JavaScript meetup, and organizes the AI inaction discussion group through Latent Space.
Please click here to see the transcript of this episode.
October 12, 2024
My AMA yesterday is up on YouTube
Boy, Jens Weller turns these things around quickly! Thanks again, Jens, for having me on your Meeting C++ Live show.
I’ve put a list of the questions, with timestamped links, below…
All the questions and answers, with links00:19 What are you up to with C++ currently / what keeps you excited?
04:04 Sean Baxter has finally written up a proposal to bring borrow checking to C++, to improve safety. What are your views on his proposal and what approach is Cpp2 planning?
08:48 Is there a long-term vision for C++? How can C++ maintain its relevance in the next 20 years?
13:14 What is your favorite C++ editor/IDE when not using Microsoft Visual Studio?
14:19 Will networking be in C++26?
17:43 Why is MSVC 2022 falling behind Clang and GCC on C++23 and C++26 features?
21:21 What is the roadmap for Cpp2? Whether it will be fit for production use?
26:30 Should the stdlib be split in two parts. One with slow changes and one with fast changes. E.g., ranges were introduced in C++20 but finished in C++23. I am still missing some features.
29:34 Are there plans to address ABIs with interfaces or other features in C++?
36:18 What is your answer to the growing complexity of C++ to be learned especially by novices? How would we teach C++ (e.g., at the university) if it gets larger and larger?
40:53 In the context of C++’s zero-cost abstractions philosophy, how do you see future proposals for making bounds checking in std::vector both safer and more efficient?
47:13 Are C++ safety initiatives arriving too late to fend off the growing adoption of Rust for “safe” low-level development?
55:25 What is the status of the profiles proposal in C++? Will some of it be part of C++26 or C++29?
57:35 The Common Package Specification, which looked very promising, seems stalled. Why is tooling in the language not a priority?
59:11 What do you think of std::execution / P2300R10? The API changed a lot across papers, and to me is quite a piece of work for library implementers to integrate.
1:04:35 Aren’t you afraid that reflection might be misused too much (e.g., use it for serialization)?
1:06:46 If local uninitialized variables are no longer UB, how will they behave? Could you please elaborate a bit on that?
1:11:30 How is the Contracts TS coming along? What are your thoughts on Contract Based Programming, in general?
1:15:56 Any chance of having type erasure (mainly std::any) in MSVC reimplemented not on top of RTTI? Unfortunately the current implementation makes it unusable in places where symbol names are left behind by RTTI.
1:17:38 What happened with the official publication of the C++23 standard?
1:22:31 Preview of my keynote next month at Meeting C++.
October 10, 2024
Live AMA tomorrow (Friday): Meeting C++ Online

Tomorrow I’ll be joining Jens Weller for a live AMA on “Meeting C++ online.”
The coordinates are:
Link: Meeting C++ live with Herb Sutter+ live on LinkedIn
+ live on FacebookDate: Friday 2024-10-11Time: social hour starts at 19:00 CEST, AMA starts at 20:00 CEST
I’m looking forward to your technical questions about C++26 evolution, cppfront, Rust, reflection, safety and security, concurrency and parallelism, and software in general… and optionally questions about SF/fantasy novels and cinema, but then I might ask some similar questions in return.
Then next month I’m also looking forward to speaking at Meeting C++ 2024 in person in Berlin, and seeing many of you there — thanks again, Jens, for kindly inviting me to both.
September 17, 2024
My CppCon keynote yesterday is available on YouTube
Yesterday I gave the opening talk at CppCon 2024 here in Aurora, CO, USA, on “Peering Forward: C++’s Next Decade.” Thanks to the wonderful folks at Bash Films and DigitalMedium pulling out all the stops overnight to edit and post the keynote videos as fast as possible, it’s already up on YouTube right now, below!
Special thanks to Andrei Alexandrescu for being willing to come on-stage for a mini-panel of the two of us talking about reflection examples and experience in various languages! That was a lot of fun, and I hope you will find it informative.
If you’re here in town at CppCon, there are over a dozen Reflection and Safety/Security talks this week that I mention in my talk. In particular, tomorrow (Wednesday) don’t miss Amanda Rousseau’s keynote on Security and in the afternoon Andrei’s own talk on Reflection, and then on Friday afternoon to cap off CppCon 2024 be there for Daveed Vandevoorde’s closing keynote on Reflection.
If you aren’t able to be here at CppCon this week, you can still catch all the keynote videos (and tonight’s Committee Fireside Chat) this week and early next week on YouTube, because they’re being rush-expedited to be available quickly for everyone; just watch the CppCon.org website, where each one will be announced as soon as it’s available. And, as always, all the over 100 session videos will become freely available publicly over the coming months for everyone to enjoy.
September 3, 2024
Reader Q&A: What’s the best way to pass an istream parameter?
Here’s a super simple question: “How do I write a parameter that accepts any non-const std::istream argument? I just want an istream I can read from.” (This question isn’t limited to streams, but includes any similar type you have to modify/traverse to use.)
Hopefully the answer will be super simple, too! So, before reading further: What would be your answer? Consider both correctness and usability convenience for calling code.
.
.
OK, have you got an answer in mind? Here we go…
Spoiler summary:Today’s Option 0 is simple and fine. We can hopefully do it even more simply and elegantly in C++26/29.Pre-C++11 answerI recently received this question in email from my friend Christopher Nelson. Christopher wrote, lightly edited (and adding an “Option 0” label):
I have a function that reads from a stream:
// Call this Option 0: A single functionvoid add_from_stream( std::istream &s ); // A
Pause a moment — is that the answer you came up with too? It’s definitely the natural answer, the only reasonable pre-C++11 answer, and simple and clean…
Usability issue, and C++11-26 answers… But, as Christopher points out, it does have a small usability issue, which leads to his question:
Testing code may create a temporary [rvalue] stream, and it would be nice to not have to make it a named variable [lvalue], but this doesn’t work with just the above function:
add_from_stream( std::stringstream(some_string_data) ); // ERROR, can't pass rvalue to A's parameterSo I want to add an overload that takes an rvalue reference, like this:
void add_from_stream( std::istream &&s ); // B // now the above call would work and call B
That nicely sets up the motivation to overload the function. [1]
The core of the emailed question is: How do we implement functions A and B to avoid duplication?
The logic is exactly the same, so I just want one function to forward to another.
Would it be better to do this:
// Option 1: Make B do the work, and have A call Bvoid add_from_stream( std::istream &&s ) { // B // do work.}void add_from_stream( std::istream &s ) { // A add_from_stream( std::move(s) );}Or this:
// Option 2: Have A do the work, and have B call Avoid add_from_stream( std::istream &s ) { // A // do work.}void add_from_stream( std::istream &&s ) { // B add_from_stream( s );}They both seem to work, and the compiler doesn’t complain.
It’s true that both options “work” in the sense that they compile. But, as Christopher knows, there’s more to correctness than “the compiler doesn’t complain”!
Before reading further, pause and consider… how would you answer? What are the pros and cons of each option? What surprises or pitfalls might they create? Are there any other approaches you might consider?
.
.
Christopher’s email ended by actually giving some good answers, but with uncertainty:
I think [… … …] [But] I’ve been surprised lately by some experiments with rvalue references. So I thought I would check with you.
I think it’s telling that (a) he actually does have a good intuition and answer about this, but (b) rvalue references are subtle enough that he’s uncertain about it and second-guessing his correct C++ knowledge. — Perhaps you can relate, if you’ve ever said, “I’m pretty sure the answer is X, but this part of C++ is so complex that I’m not sure of myself.”
So let’s dig in..
Option 1: Have & call && (bad)Christopher wrote:
I think the first option looks weird, and I think it may possibly have weird side-effects.
Bingo! Correct.
If Option 1 feels “weird” to you too, that’s a great reaction. Here it is again for reference, with an extra comment:
// Option 1: Make B do the work, and have A call Bvoid add_from_stream( std::istream &&s ) { // B // do work.}void add_from_stream( std::istream &s ) { // A add_from_stream( std::move(s) ); //In Option 1, function A is taking a modifiable argument and unconditionally calling std::move on it to pass it to B. Remember that writing std::move doesn’t move, it’s just a cast that makes its target a candidate to be moved from, which means a candidate to have its entire useful state stolen and leaving the object ’empty.’ Granted, in this case as long as function B doesn’t actually move from the argument, everything may seem fine because, wink wink, we happen to know we’re not actually going to steal the argument’s state. But it’s still generally questionable behavior to go around calling std::move on other people’s objects, without even a hint in your API that you’re going to do that to them! That’s like pranking random strangers by tugging on their backpack zippers and then saying “just kidding, I didn’t actually steal anything this time!”… it’s not socially acceptable, and even though you’re technically innocent it can still lead to getting a broken nose.
So avoid this shortcut; it sets a bad example for young impressionable programmers, and it can be brittle under maintenance if the code changes so that the argument could actually get moved from and its state stolen. Worst/best of all, I hope you can’t even check that code in, because it violates three C++ Core Guidelines rules in ES.56 and F.18, which makes it a pre-merge misdemeanor in jurisdictions that enforce the Guidelines as a checkin gate (and I hope your company does!).
Function A violates this rule (essentially, ‘only std::move from rvalue references’):
ES.56: Flag when std::move is applied to other than an rvalue reference to non-const.Function B violates these rules:
F.18: Flag all X&& parameters (where X is not a template type parameter name) where the function body uses them without std::move.ES.56: Flag functions taking an S&& parameter if there is no const S& overload to take care of lvalues.It’s bad enough that we already have to teach that std::move is heavily overused (for example, C++ Core Guidelines F.48). Creating still more over-uses is under-helpful.
Option 2: Have && call & (better)For Option 2, Christopher notes:
I imagine that the second one works because, as a parameter the rvalue reference is no longer a pointer to a prvalue, so it can be converted to an lvalue reference. Whereas, in the direct call site it is not.
Bingo! Yes, that’s the idea.
Recall Option 2, and here “function argument” mean the thing the caller passes to the parameter, and “function parameter” means its internal name and type inside the function scope:
// Option 2: Have A do the work, and have B call Avoid add_from_stream( std::istream &s ) { // A // do work.}void add_from_stream( std::istream &&s ) { // B add_from_stream( s );}Note that function B only accepts rvalue arguments (such as temporary objects)… but once we’re inside the body of B the named parameter is now an lvalue, because it has a name! Why? Here’s a nice explanation from Microsoft Learn:
Functions that take an rvalue reference as a parameter treat the parameter as an lvalue in the body of the function. The compiler treats a named rvalue reference as an lvalue. It’s because a named object can be referenced by several parts of a program. It’s dangerous to allow multiple parts of a program to modify or remove resources from that object.
So the argument is an rvalue (at the call site), but binding it to a parameter name makes the compiler treat it as an lvalue (inside the function)… so now we can just call A directly, no fuss no muss. Conceptually, function B is implicitly doing the job of turning the argument into an lvalue, and so serves its intended purpose of expressing, “hey there, this overload set accepts rvalues too.”
And that’s… fine.
It’s still not “ideal,” though, for two reasons. First, astute readers will have noticed that this only addresses two of the three C++ Core Guidelines violations mentioned above… the new function B still violates this rule:
ES.56: Flag functions taking an S&& parameter if there is no const S& overload to take care of lvalues.The reason this rule exists is because functions that take rvalue references are supposed to be used as overloads with const& to optimize “in”-only parameters. We could shut up the stupid checker eliminate this violation warning by adding such an overload, and mark it =delete since there’s no other real reason for it to exist (consider: how would one use a const stream?). So to get past a C++ Core Guidelines checker we would actually write this:
The second reason this isn’t ideal is that having to write an overload set of two or three functions is annoying at best, because we’re just trying to express something that seems awfully simple: “I want a parameter that accepts any non-const std::istream argument“… that shouldn’t be hard, should it?
Can we do better? Yes, we can, because those aren’t the only two options.
Option 3: Write a single function using a forwarding reference (a waypoint to the ideal)In C++, without using overloading, yes we can write a parameter that accepts both lvalues and rvalues. There are exactly three options:
Pass by value (“in+copy”)… but for streams this is impossible, because streams aren’t copyable.Pass by const& (“in”)… but for useful streams this is impossible, because streams have to be non-const to be read from (reading modifies the stream’s position).Pass by T&& forwarding reference … … … ?“Exclude the impossible and what is left, however improbable, must be the truth.”
A.C. Doyle, “The Fate of the Evangeline,” 1885.
Today, the C++11-26 truth is that the way to express a parameter that can take any istream argument (both lvalues and rvalues), without using overloading, is to use a forwarding reference: T&& where T is a template parameter type…
Wait! You there, put down the pitchfork. Hear me out.
Yes, this is complex in three ways…
It means writing a template. That has the drawback that the definition must be visible to call sites (we can’t ship a separately compiled function implementation, except to the extent C++20 modules enable).We don’t actually want a parameter (and argument) to be just any type, so we also ought to constrain it to the type we want, std::istream.Part of the forwarding parameter pattern is to remember to correctly std::forward the parameter in the body of the function. (See C++ Core Guidelines F.19.)Here’s the basic pattern:
// Option 3(a): Single function, takes lvalue and rvalue streamstemplate void add_from_stream( S&& s ) requires std::is_same_vFor reasons discussed in Barry Revzin’s excellent current proposal paper P2481 (more on that in a moment), we actually want to allow arguments that are of derived type or convertible type, so instead of is_same we would actually prefer is_convertible:
// Option 3(b): Single function, takes lvalue and rvalue streamstemplate void add_from_stream( S&& s ) requires std::is_convertible_v“Seriously!?” you might be thinking. “There’s no way you’re going to teach mainstream C++ programmers to do that all the time! Write a template? Write a requires clause?? And write std::forward(s) with its pitfalls (don’t forget )??? Madness.”
Yup, I know. You’re not wrong.
Which is why I (and Barry) believe this feature needs direct language support…
Post-C++26: Simple, elegant, cleanRemember I mentioned Barry Revzin’s current paper P2481? Its title is “Forwarding reference to specific type/template” and it proposes this exact feature.
And astute readers will recall that my Cpp2 syntax already has this generalized forward parameter passing style that also works for specific types, and I designed and implemented that feature in cppfront and demo’d it on stage at CppCon 2022 (1:26:28 in the video):

See Cpp2: Parameters for a summary of the Cpp2 approach. In a nutshell: You declare “what” you want to do with the parameter, and let the compiler do the mechanics of “how” to pass it that we write by hand today. Six options, one of which is forward, are enough to cover all kinds of parameters.
As you see in the accompanying CppCon 2022 screenshot, the forward parameters compiled to ordinary C++ that followed Option 3(a) above. A few days ago, I relaxed it to compile to Option 3(b) above instead, as Barry suggested and another Cpp2 user needed (thanks for the feedback!), so it now allows polymorphism and conversions too.
So in Cpp2 today, all you write is this:
// Works in Cpp2 today (my alternative experimental simpler C++ syntax)add_from_stream: ( forward s: std::istream ) = { // do work.}… and cppfront turns that into the following C++ code that works fine today on GCC 11 and higher, Clang 12 and higher, MSVC 2019 and higher, and probably every other fairly recent C++ compiler:
auto add_from_stream(auto&& s) -> void requires (std::is_convertible_v&>){ // do work + automatically adds "std::forward" to the last use of s}Write forward, and the compiler automates everything else for you:
Makes that parameter a generic auto&&.Adds a requires clause to constrain it back to the specific type.Automatically does std::forward correctly on every definite last use of the parameter.It’s great to have this in Cpp2 as a proof of concept, but both Barry and I want to make this easy also in mainstream ISO C++ and are independently proposing that C++26/29 support the same feature, including that it would not need to be implicitly a template. Barry’s paper has been strongly encouraged, and I think the only remaining question seems to be the syntax (see the record of polls here), which could be something like one of these:
// Proposed for C++26/29 by Barry's paper and mine// Possible syntaxesvoid add_from_stream( forward std::istream s );void add_from_stream( forward: std::istream s );void add_from_stream( forward s: std::istream );// or something else, but NOT "&&&" // -- happily the committee said "No!" to thatIn Cpp2, forward parameters with a specific type have proven to be more useful than I originally expected, and that’s even though they compile down to a C++ template today… one benefit of adding this feature directly into the language is that a forwarding reference to a concrete type could be easily implemented as a non-template if it’s part of C++26/29.
My hope for near-future C++ is that the simple question “how do I pass any stream I can read from, even rvalues?” will have the simple answer “as a forward std::istream parameter.” This is how we can simplify C++ even by adding features: By generalizing things we already have (forwarding parameters, to work also for specific types) and enabling programmers to directly express their intent (say what you mean, and the language can help you). Then even though the language got more powerful, C++ code has become simpler.
In the meantime, in today’s C++, consider either Option 0 or Option 3 as the direct way to write a function that takes a stream without having to write any overloads.
Thanks again to Christopher Nelson for this question!
.
Notes[1] For simpler cases if you’re reading a homogenous sequence of the same type, you could try taking a std::ranges::istream_view. But as far as I know, that approach doesn’t help with general stream-reading examples.
August 7, 2024
Reader Q&A: What does it mean to initialize an int?
Acknowledgments: Thanks to Jens Maurer, Richard Smith, Krystian Stasiowski, and Ville Voutilainen, who are all ISO C++ committee core language experts, for helping make my answer below correct and precise.
I recently got this question in email from Sam Johnson. Sam wrote, lightly edited:
Given this code, at function local scope:
int a;a = 5;So many people think initialization happens on line 1, because websites like cppreference defines initialization as “Initialization of a variable provides its initial value at the time of construction.”
However, I’m convinced the initialization happens on line 2, because [various good C++ books] define initialization as simply the first meaningful value that goes into the variable.
Could you please tell me which line is considered initialization?
That’s a great question. Cppreference is correct, and for all class types the answer is simple: The object is initialized on line 1 by having its default constructor called.
But (and you knew a “but” was coming), for a local object of a fundamental built-in type like int, the answer is… more elaborate. And that’s why Sam is asking, because Sam knows that the language has kind of loose about initializing such local objects, for historical reasons that made sense at the time.
Short answer: Saying the variable gets its initial value on line 2 is completely reasonable. But note that I deliberately didn’t say “the object is initialized on line 2,” and both the code and this answer gloss over the more important problem of: “Yeah, but what about code between lines 1 and 2 that could try to read the object’s value?”
This post has three parts:
Pre-C++26, yes, this is kind of awkward. But the funniest part is how Standard describes it today, which is just begging for a little in-good-fun roasting, and so I’ll indulge.In C++26, we make this code safe by default, thanks to Thomas Köppe! This is a Big Deal.In my Cpp2 experiment, this problem disappears entirely, and all types are treated equally with guaranteed initialization safety. My aim is to propose this for ISO C++ itself post-C++26, so ISO C++ could evolve to remove this issue too in the future, if there’s consensus for such a change.Let’s start with the world today, our pre-C++26 status quo…
Pre-C++26 answer: The variable is never “initialized”For those few built-in types like int, the answer is that in this example there is no initialization at all, because (technically) neither line is an initialization. If that surprises you, consider:
Line 1 declares an uninitialized object. There is no initial value at all, explicit or implicit.Line 2 then assigns an “initial value.” This overwrites the object’s bits and happens to give the object the same value as if its bits had been initialized that way on line 1… but it’s an assignment, not an initialization (construction).That said, I think it’s reasonable to informally call line 2 “setting an initial value,” in the sense that it’s the first program-meaningful value put into that object. It’s not formally an initialization, but the bits end up the same, and good books can reasonably call line 2 “initializing a.”
“But wait,” I hear someone in the back saying, “I read the Standard last night, and [dcl.init] says that line 1 is a ‘default-initialization’! Therefore line 1 is an initialization!” Yes, and no, respectively. So let’s look at the Standard’s formal precise and quite funny answer, and this is truly a delightful thing to read: The Standard does say that in line 1 the object is indeed default-initialized… but, for types like int, the term “default-initialized” is defined to mean “no initialization is performed.”
I am not making this up. See [dcl.init] paragraph 7.
(This may be a good time to mention that “the Standard is not a tutorial”… in other words, we wouldn’t read the Standard to learn the language. The Standard is quite precise about telling us what a C++ compiler does, and there’s nothing really wrong with the Standard specifying things in this way, it’s totally fine and it totally works. But it’s not written for a lay reader, and nobody would blame you if you thought that “default-initialization [means] no initialization is performed” sounds like cognitive dissonance in action, Orwellian doublethink (which is not the same thing), passive-aggressive baiting, or just garden-variety Humpty Dumptyism.)
A related question is: After line 1, has the object’s lifetime started? The good news is that yes it has… in line 1, the uninitialized object’s lifetime has indeed started, per [basic.life] paragraph 1. But don’t let’s look too closely at that paragraph’s words about “vacuous initialization,” because that’s yet another fancyspeak in the Standard for the same concept of “initialized but, ha ha, just kidding.” (Have I mentioned that the Standard isn’t a tutorial?) And of course it’s a serious problem that the object’s lifetime has started, but it hasn’t been initialized with a predictable value… that’s the worst problem of an uninitialized variable, that it can be a security risk to read from it, which has been true “undefined behavior” that could do anything, and attackers can exploit this property.
Fortunately, this is where the safety story gets significantly better, in C++26…
C++26: It gets better (really!) and safe by defaultJust a few months ago (the March 2024 meeting in Tokyo), we actually improved this for C++26 by adopting Thomas Köppe’s paper P2795R5, “Erroneous behavior for uninitialized reads.” If that sounds familiar to readers of this blog, it may be because I highlighted it in my Tokyo trip report.
C++26 has created the new concept of erroneous behavior, which is better than “undefined” or “unspecified” because it gives us a way to talk about code that is literally “well-defined as being Just Wrong”… seriously, that’s almost a direct quote from the paper… and because it’s now well-defined it gets stripped of the security scariness of “undefined behavior.” Think of this as the Standard having a tool to turn some behavior from “scarily undefined” to merely “tsk, we know this is partly our fault because we let you write this code and it doesn’t mean what it should mean, but you really wrote a bug here, and we’re going to put some guard-rails around this pit of snakes to remove the safety risk of you falling into it by default and our NSA/CISA/NIST/EO insurance premiums going up.” And the first place that concept has been applied has been to… drum roll… uninitialized local variables.
This is a big deal, because it means that the original example’s line 1 is now still uninitialized, but since C++26 it’s “erroneous behavior” which means that when the code is built with a C++26 compiler, undefined behavior cannot happen if you read the uninitialized value. Yes, that implies a C++26 compiler will generate different code than before… it will be guaranteed to write an erroneous value the compiler knows (but that isn’t guaranteed to be one the programmer can rely on; so don’t rely on it being zero) if there’s any possibility that value might be read.
This may seem like a small thing, but it’s already a major improvement, and shows that the committee is serious about actively changing our language to be safe by default. Making more and more code safe by default is a trend you can expect to see a lot more of in C++’s medium-term future, and that’s a very welcome thing.
While you wait for your favorite C++26 compiler to add this support, you can get an approximation of this feature today with the GCC or Clang switch -ftrivial-auto-var-init=pattern or the with MSVC switch /RTC1 (run, don’t walk, to use those now if you can). They get you most of what C++26 gives, except that they may not emit a diagnostic (e.g., the Clang switch emits a diagnostic only if you’re running Memory Sanitizer).
For example, consider how this new default prevents secrets from leaking, in this program compiled with and without today’s flag (Godbolt link):
#include auto f1() { char a[] = {'s', 'e', 'c', 'r', 'e', 't', '\0' }; std::cout << a << "\n";}auto f2() { char a[8]; std::cout << a << "\n"; // today this likely prints "secret"}auto f3() { char a[] = {'0', '1', '2', '3', '4', '5', '\0' }; std::cout << a << "\n"; // overwrites "secret" (if only just)}int main() { f1(); f2(); f3();}Typically, all three local arrays will reuse the same stack storage, and after f1 returns the string secret is likely still sitting on the stack, waiting for f2‘s array to overlay it.
In today’s C++ by default, without -ftrivial-auto-var-init=pattern or /RTC1, f2 will likely print secret. Which is… um (looks at feet and twists a toe to pretend to erase an imaginary spot on the floor)… let’s say problematic for safety and security. As Jon would say to today’s undefined-behavior uninitialized rule, “you give C++ a bad name.”
But with GCC and Clang -ftrivial-auto-var-init=pattern, with MSVC /RTC1, and in C++26 onward by default, f2 will not leak the secret. As Bjarne has sometimes said in other contexts, but I think applies here too: “This is progress!” And to any grumpy readers who may be inclined to say, “dude, I’m used to insecure code, getting rid of insecure code by default isn’t in the spirit of C++,” well, (a) it is now, and (b) get used to it because there’s a lot more like this on the way.
But this is C++, you always have the full power to take control and get maximum performance when you need to. So yes, if you really want, C++26 will let you opt out by writing [[indeterminate]], but every use of that attribute should be challenged in every code review and require justification in the form of clear performance measurements showing that you need to override the safe default:
int a [[indeterminate]] ; // C++26-speak for "yes please hurt me, // I want the bad old dangerous semantics"Post-C++26: What more could we do?So this is where we are pre-C++26 (highlighting the most problematic lines):
// In today’s C++ pre-C++26, for local variables// Using a fundamental type like 'int'int a; // declaration without initializationstd::cout << a; // undefined: read of uninitialized variablea = 5; // assignment (not initialization)std::cout << a; // prints 5// Using a class type like 'std::string'string b; // declaration with default constructionstd::cout << b; // prints "": read of default constructed valueb = "5"; // assignment (not initialization)std::cout << b; // prints "5"Note that line 5 might not print anything… it’s undefined behavior, so you’d be lucky if it’s just a matter of printing something or not, because a conforming compiler could technically generate code to erase your hard drive, invoke nasal demons, or other traditional UB nastiness.
And here is where we are starting in C++26 (differences highlighted):
// In C++26, for local variables// Using a fundamental type like 'int'int a; // declaration with some erroneous valuestd::cout << a; // prints ???: read of erroneous valuea = 5; // assignment (not initialization)std::cout << a; // prints 5// Using a class type like 'std::string'string b; // declaration with default constructionstd::cout << b; // prints "": read of default constructed valueb = "5"; // assignment (not initialization)std::cout << b; // prints "5"The good news: Our hard drives and noses are now safe from erasure and worse in line 5.
The fine print: C++26 compilers are required to make line 4 write a known value over the bits, and they are encouraged (but are not required) to tell you line 5 is a problem.
In my Cpp2 experimental syntax, local variables of all types are defined like a: some_type = initial_value;. You can omit the = initial_value part to express that stack space is allocated for the variable but its actual initialization is deferred, and then Cpp2 guarantees initialization before use; you must do the initialization later using = (e.g., a = initial_value;) before any other use of the variable, which gives you the flexibility of doing things like using different constructors for the same variable on different branch paths. So the equivalent example is (differences from C++26 highlighted):
// In my Cpp2 syntax, local variables// Using a fundamental type like 'int'a: int; // allocates space, no initialization// std::cout << a; // illegal: can't use-before-init!a = 5; // construction => real initialization!std::cout << a; // prints 5// Using a class type like 'std::string'b: string; // allocates space, no initialization// std::cout << b; // illegal: can't use-before-init!b = "5"; // construction => real initialization!std::cout << b; // prints "5"Cpp2 deliberately has no easy way to opt out and use a variable before it has been initialized. To get that effect, you’d have to have an array of raw std::bytes or similar on the stack, and do an unsafe_cast to pretend it’s a different type… which is verbose and hard to write, and that’s because I think that unsafe code should be verbose and hard to write. I may disapprove of unsafe code you may write in the name of performance, but I defend to the death your right to write it when you need to; that’s core to C++.
So far, I find this model is working very well, and it has the triple benefits of performance (initialization work is never done until you need it), flexibility (I can call the real constructor I want), and safety (it’s always real “initialization” with real construction, and never any use-before-initialization). I think we could have this someday in ISO C++, and I intend to bring a proposal along these lines to the ISO C++ committee in the next year or two, and I’ll be as persuasive as I can. They might love it, they might find flaws I’ve overlooked, or something else… we’ll see! In any event, I’ll be sure to report any progress here.
Thanks again to Sam Johnson for this question!
Herb Sutter's Blog
- Herb Sutter's profile
- 32 followers
