The Problem with C++ Safety: Modern C++ Is a Mindset, Not a Standard
4 points by nrposner
4 points by nrposner
"Modern C++" is a meme at this point. When C++11 came out, everyone talked about modern C++. Back then, it was justified - move semantics were a real and significant innovation. But here we are, one and a half decades later, still talking about "modern C++". Nobody can tell me what "modern C++" is anymore. Is it modern C++ if I use C++20's spaceship operator? Or if I use C++23's std::print? Compared to other languages, nothing about C++ feels modern anymore. Not the type system, not the memory handling and lifetimes, not the toolchains, not the build environment and dependency management. If anything, we've been waiting for decades for features that other languages had all along. Some shipped, like concepts and modules, though module support is apparently still very spotty. Other absolutely crucial features like compile-time reflection and memory safety are still completely unavailable. Perhaps we should start calling it "contemporary C++" to make explicit the fact that nothing about it is modern and that by now, everyone should use the primitives that were new decades ago.
We saw the first part of this post a couple weeks ago, and I was hoping there was something more substantive in part 2.
On the one hand, I’m certainly in agreement that C++ education materials could improve: I sometimes wonder how many person-hours of work could be avoided by revamping the UI/UX of cppreference.com.
All the same, declaring that it’s all about mindset and that to be safe people should just use more standard library functions feels quite weak. Those with experience should correct me if I’m wrong, but talking to the C++ devs in my life tells me that there’s a long memory of being bitten by innocent-looking standard library constructs, both in safety and the performance that a lot of C++ projects call for.
You are right but moreover.
We have tried better education for 30years. Guess what?
The problem is still there. At some point it is time to wonder why this magical solution we keep using does not produce good outcomes!
Meanwhile, in Rust, we got an actual factual use case at large scale, in real industry settings and... It worked.
So uh. One side has 30years of not achieving anything. And the other managed to make it work in less than 20 years starting from scratch.
Those with experience should correct me if I’m wrong, but talking to the C++ devs in my life tells me that there’s a long memory of being bitten by innocent-looking standard library constructs, both in safety and the performance that a lot of C++ projects call for.
Yep. In fact, the form of std::equal used in this post is one of those constructs! It's one of a number of algorithms referred to as "three-legged": One range is given by begin, end iterators but the other only by begin. That means that accesses to the second range cannot be bounds-checked, yielding UB on out-of-bounds.
In this specific case (the palindrome check), it happens to work out fine, but not for the reasons given in the post:
There are none, right? And it is so easy to verify: no if statements — in fact, no conditionals at all, no pointers or indices, no explicit loop. It’s just a single, simple function call. So this code is correct by inspection. It is correct by default. And it is simple. And fast.
This is wrong, and dangerously so. This might even give a beginner the idea that std::equal is inherently safe, when it isn't. The real reason that there is no UB is that the math happens to work out s.t. you never go out-of-bounds. If you replace /2U with *2U in the example, you have UB due to out-of-bounds. This is already the level of safety we get from "bad old C++", and in fact from C: Don't mess up the math, and you're safe from OOB.
The std::ranges version used later in the post is "four-legged", and indeed safe from this problem. I think the post should at least mention that, in the interest of educating C++ users about memory safety.
Well, what else could it be?
Its not as if C++ made significant progress towards becoming safer in C++26. In fact they do not even want to become memory-safe, just "memory-safer", whatever that is even supposed to mean.