10 December 2025

Cthulhu almost tamed, Cthulhu expert announces

Back in March, Herb Sutter posted a long blog post about progress taming the undefined behavior in C++.

Herb Sutter is great, the progress he's reporting seems really important to me, and I applaud all the efforts documented there. However, I feel like the post has some misleading parts and someone should point them out.

(1) Since C++11 in 2011, more and more C++ code has already become UB-free. Most people just didn’t notice.

  • Spoiler: All constexpr/consteval compile-time code is UB-free. As of C++26 almost the entire language and much of the standard library is available at compile time, and is UB-free when executed at compile time (but not when the code is executed at run time, hence the following additional work all of which is about run-time execution).

It is really significant to have a compile-time mode that's defined in the standard and includes most of the langugage. If you haven't played with it, you should. C++ compilers now include a C++ interpreter that you can use to interrogate the language. Rust has this for a subset of the language (minus the standard) and it's been super useful.

It's very strange to ask readers to believe this has had a significant impact on the safety of C++ code in the real world. The use of the phrase "more and more C++ code" reads that way to me, at least. But of course practically all C++ code runs at run time, not at compile time. That's why they call it run time. An example of something you can't do in constexpr code at compile time, even in C++26, is open a file. Or react to user input in any way.

If successful, these steps would achieve parity with the other modern memory-safe languages as measured by the number of security vulnerabilities, which would eliminate any safety-related reasons not to use C++.

The steps in question are, in my words:

  1. Standardize compile-time constexpr/consteval without UB, as described above. (done!)
  2. Ratify C++26 to add contracts, eliminate UB from uninitialized locals, and add optional standard library hardening.
  3. Catalog and address all the other run-time UB.

Needless to say, step 3 is the lion's share of the work. It's the closest thing to the “draw the rest of the owl” meme I've ever seen in real life.

I'm not saying this to downplay the work that went into C++26. Again, I actually do think that work is smart and important. In particular, Thomas Köppe's work on uninitialized reads is great stuff. There's been a sea change: the C++ standard committee as a whole is interested in reducing UB and making the language a productive partner in the work of building secure systems. That hasn't always been the case.

I appreciate Herb Sutter's positive attitude and I think everything that can be done for C++ should be done. I can imagine a future where C++ can be memory-safe, with acceptable run-time costs. But let's be honest about how long this road is and how far along it we can see.

No comments: