lobsters score: 2 · tags: c++
“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.
score 1 · canonical
While move semantics in C++11 were a significant addition to the language, I hesitate to call them an innovation: They have been understood for decades at that point.
score 1 · canonical
But which language got it right before C++? (Okay, getting it right is a bit of an exaggeration, there’s this whole rabbit hole with “what’s inside a moved-from object?”, but overall it was a pretty good initial implementation.) There’s a long way between academia and actual code being compiled and running in prod.
score 1 · canonical
C++ did not get move semantics right. They debated it at the time, but it’s quite clear today that moves should be destructive. It’s true there wasn’t obvious prior art, but FP languages had linear and unique types since the 80s, that should have provided a good hint.
score 1 · canonical
C++ went for the only approach they could integrate somewhat easily, not for the right one:-)
Yeap, it is a long way from academia to something running in prod, but still the innovation happens in academia. I can not see anything innovative in taking results academia came up decades ago and applying it to a language. It is of course still a big thing for a language to get ideas from the ivory tower adapted to work in reality:-)
score 1 · canonical
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.
score 1 · canonical
Contracts (which are abput to be cut out again), something for profiles, safe C++ (which was killed in the C++26 cycle). Basically anything that was brought up:-)
You might argue that they managed to get library hardening on the way, but that is something I used in 1998. The new thing is not the functionality, just that compilers are rrquired to provide it.
score 1 · canonical
The impression that I had was that contracts’ safety benefits are quite contentious. IIRC there were multiple NB comments asserting that contracts reduce safety, and given the most recent vote I’d imagine those concerns weren’t sufficiently addressed.
The impression I had of the “simpler” profiles is similar to your description of hardening: that those profiles are effectively standardizing already-existing functionality.
Safe C++ was interesting, but given that even the simpler profiles didn’t make the C++26 cutoff I doubt Safe C++ would have made it even if the committee decided to prioritize it over profiles.
score 1 · canonical
Contracts do what they were designed to do, with a bunch of extra bells and whistles added that are problematic in that they do not actually evaluate the contracts, but that have been on the list of requirements for easier adoption and were discussed thoroughly when that got added.
Of course you can also get into trouble when you build different TUs with different enforcement settings, just like you get into trouble when you mismatch compiler flags for TUs in general.
Anyway: I do not have the impression safety and especially the memory safety regulators strongly suggest has any priority in C++, not even with the CRA already starting to take effect in Europe. Last-minute removal of the one safety feature they announced is probably not going to build trust.
Profiles promised to do way more than compilers can do now… Even more than dedicated static analysis tools can manage. But so far implementations are very incomplete and experimental, so we will see what we can get in the end.
score 2 · canonical
and were discussed thoroughly when that got added.
Apparently that (and subsequent) discussion wasn’t thorough enough to convince the naysayers :/
Of course you can also get into trouble when you build different TUs with different enforcement settings, just like you get into trouble when you mismatch compiler flags for TUs in general.
From my understanding this is a bit of an intractable problem. The current behavior leads to objections because some people feel strongly that C++ shouldn’t be adding more footguns, especially of the ODR-ish/spooky-action-at-a-distance type. On the other hand IIRC the standard doesn’t really have great tools to specify other solutions and other solutions come with their own tradeoffs (e.g., including contracts in mangling impacts ABI)
Anyway: I do not have the impression safety and especially the memory safety regulators strongly suggest has any priority in C++
I think the fact that at least some work is happening in that direction should provide at least a glimmer of hope.
Profiles promised to do way more than compilers can do now…
Right, which is why I used the “simpler” qualifier - those we at least have some prior experience with. Who knows with the more adventurous profiles.
score 1 · canonical
From my understanding this is a bit of an intractable problem. The current behavior leads to objections because some people feel strongly that C++ shouldn’t be adding more footguns, especially of the ODR-ish/spooky-action-at-a-distance type.
We will see whether or not that was a serious argument soon: Profiles are supposed to inject runtime checks into code, so they will have exactly the same problem. I doubt we will hear much of the possible ODR violations they can introduce.
But to be fair, Gabriel already mentioned improving linkers in one of his recent presentations on profiles… maybe they will prescribe improved linkers? But who will care to write those?
Progress on C++ features is slow right now. Many sponsors reduced their commitments. In the last year we saw one company tour C++ events, promissing to invest heavily into the language, and we just killed (or at least seriously delayed) that companies favorite feature. And who will fix the contracts mess we have now? I’d be seriously surprised to see anyone involved with contracts to stay around now. Yet more brain drain… this year there were so many speakers that I know from c++ conferences presenting at rust events.
e.g., including contracts in mangling impacts ABI
That was considered and universally rejected from what I saw described in presentations.
I think the fact that at least some work is happening in that direction should provide at least a glimmer of hope.
We are 10+ years after rust 1.0 and 5+ years past governments starting to build pressure towards memory-safe languages. What we got to show so far is a decision to not change C++ too much (no viral annotatioms) and ideas for new debugging tools (profiles, library hardening, …) that we hope to be able to sell for more than they are.
That we promised contracts as a important step for more safety and the basis for further steps after C++23 was under-delivering on safety and then could not deliver that in such a spectacular way does not make it easier for regulators to trust in C++ either.
score 1 · canonical
Profiles are supposed to inject runtime checks into code, so they will have exactly the same problem. I doubt we will hear much of the possible ODR violations they can introduce.
Oh, that’s an interesting point! Now I’m quite curious to see what the answer(s) will be.
maybe they will prescribe improved linkers?
That would be quite the departure from what the standard has traditionally addressed. Not to mention that such a thing seems like it would make adoption of newer C++ standards even more bathtub-shaped than it already is.
But who will care to write those?
Given how modules have gone I can’t say I’d expect rapid changes…
That was considered and universally rejected from what I saw described in presentations.
Yes, that’s the impression I had as well, for ABI/not-our-job reasons. I just mentioned that to say that ODR-ish behavior wasn’t the only possible option.
Though apparently if improving linkers is in the cards for profiles I have to wonder why such a solution wasn’t also proposed for contracts…
What we got to show so far is a decision to not change C++ too much (no viral annotatioms) and ideas for new debugging tools (profiles, library hardening, …) that we hope to be able to sell for more than they are.
I feel like given historical attitudes and the lack of significant preexisting work that could be easily incorporated the C++ ship turning slowly isn’t too surprising. Whether it’ll turn fast enough (or whether it’ll end up mattering at all) remains to be seen, I suppose.
score 1 · canonical
Profiles are supposed to inject runtime checks into code, so they will have exactly the same problem. I doubt we will hear much of the possible ODR violations they can introduce.
Oh, that’s an interesting point! Now I’m quite curious to see what the answer(s) will be.
maybe they will prescribe improved linkers?
That would be quite the departure from what the standard has traditionally addressed. Not to mention that such a thing seems like it would make adoption of newer C++ standards even more bathtub-shaped than it already is.
But who will care to write those?
Given how modules have gone I can’t say I’d expect rapid changes…
That was considered and universally rejected from what I saw described in presentations.
Yes, that’s the impression I had as well, for ABI/not-our-job reasons. I just mentioned that to say that ODR-ish behavior wasn’t the only possible option.
Though apparently if improving linkers is in the cards for profiles I have to wonder why such a solution wasn’t also proposed for contracts…
What we got to show so far is a decision to not change C++ too much (no viral annotatioms) and ideas for new debugging tools (profiles, library hardening, …) that we hope to be able to sell for more than they are.
I feel like given historical attitudes and the lack of significant preexisting work that could be easily incorporated the C++ ship turning slowly isn’t too surprising. Whether it’ll turn fast enough (or whether it’ll end up mattering at all) remains to be seen, I suppose.
score 1 · canonical
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.
score 1 · canonical
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::equalused 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 bybegin,enditerators but the other only bybegin. 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::equalis 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/2Uwith*2Uin 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::rangesversion 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.
score 2 · canonical
Knowing this I’m now even more confused about why this is touted as superior. Sure, there are no if statements, but you’re doing pointer math on iterators and that’s supposed to be less confusing?
If someone wrote this code with begin() instead of cbegin(), the iterator might be non-const.
If the caller forgot the division, it would be wrong but not necessarily obviously so. If the caller used /2 instead of /2U… what would actually happen? Are you certain of catching that in code review?
And let’s hope the caller doesn’t try to do this in terms of end() or back(), because then you can end up with a subtle off-by-one error.
Or what if the caller uses str.length() instead of str.string()? Jk, those two should be identical… but would you have been certain of that without going to the documentation?
The ranges solution seems fine enough, but only came about a few years ago. A lot of people working in C++ today can’t even use it in their codebases.
score 1 · canonical
I concur. The way I see it, iterators (but perhaps not ranges) belong in the pile of “bad old C++”. Most of the iterators you encounter are paper-thin abstractions over pointer math that don’t actually add safety but do add mental burden.
The fact that you can write very clever, terse snippets like the palindrome example is cool, but in the absence of actual safety it’s more of a risky flex than elegance (to me).
score 2 · canonical
I’m also unsure why it leans so far into smushed-together single-letter abbreviations. Like crbegin(), one letter ‘r’ reverses the behavior, super easy to forget or miss while reading. How much time do you save by not typing ‘reverse’ or using snake_case, and how much more strain does it put on anyone doing code review?
score 1 · canonical
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.
Wow TIL this is a thing and there’s an entire family of those.
The reference tells me C++14 introduced 4-legged overloads of
std::equalbut the 3-legged versions existing in the first place seems insane, and even worse C++17 overloads for execution policy support still added 3-legged overloads.
score 1 · canonical
Not only that, there are new APIs added in later standards versions that only come in 3-legged overloads, such as
std::copyfrom C++17. There exists a 4-legged version as of C++20, but it’s in a different namespace (std::ranges::copy).In a roundabout way though, this supports the main point of the blog post: Safety in C++ is a mindset thing, and just using modern standard library APIs doesn’t give you that mindset for free. There are plenty of footguns still being added into the standard, even.
score 1 · canonical
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.
score 1 · canonical
One side has 30years of not achieving anything. And the other managed to make it work in less than 20 years starting from scratch.
I think it’s a bit of an overstatement to claim that C++ did not achieve anything in the last 30 years. A lot of innovations and learning from C++ flowed straight into the design of younger languages. I see it more as a natural evolution, a lifecycle in which C++ has passed its prime and must eventually give way to a new generation of stronger, more capable languages.
score 1 · canonical
I do not mean that C++ achieved nothing, I meant that taking Safety as a mindset and education problem has not achieved anything.
score 2 · canonical
Strictly speaking, nothing about the language physically prevents you from getting up to more or less the same shenanigans as in C/C++ with sufficient (ab)use of
unsafe. I think the reason you basically never see such a thing is precisely both because the Rust community can be described as taking “safety as a mindset” (i.e., the wider community generally Frowns Uponunsafeabuse; you can even argue that this extends to the language itself) and because the devs and the community has expended quite a bit of effort from its very start to educate new Rust devs aboutunsafe.Rust’s lack of a need to preserve backwards compatibility helps greatly here. There’s no older style and teaching material to have to build momentum against. On top of that, I think the
unsafekeyword makes surfacing/asking about these concepts a lot easier; for instance, consider a new Rust dev coming acrossmutorunsafe. They can ask what those keywords mean and under what circumstances they should be used, for which the latter would naturally lead into learning into the Rust conventions for use ofunsafe. Compare that with C++, which hasconstbut nothing likeunsafe. A new C++ dev can ask when they should useconst, but there’s no natural vocabulary to act as a springboard into learning about safety/soundness/etc.
score 1 · canonical
You explicitly show it there. It is not a mindset!
It is that doing these things are actually hard! You have to use
unsafe, you have to actually go out of your way. While some of the example in the OP are literally capably of UB silently!It is not only a mindset, the tool makes it harder to do and force you to actively search it. That is the difference. Not the mindset.
score 1 · canonical
Sorry, looking back at my comment I realize I didn’t convey my intended message clearly. I do hint at it, but it’s rather oblique.
In short, I think it’s both: the existence of
unsafeis important to achieving Rust’s results, but I don’t think it is sufficient on its own. You also need a community that takes safety concerns seriously. Similarly, I think C++ could have produced better safety outcomes if the community had historically taken safety issues more seriously even without the existence of anunsafeequivalent, but that obviously has not been the case.Consider the actix-web hoopla from a while ago; if the original author’s attitude around unsound uses of
unsafewere far more prevalent in the Rust community then while I think Rust would have shown some improvement over C/C++ with respect to memory safety error rates I don’t think the improvement would be as much as it is today.Or consider (as other commenters here have pointed out) that while it’s possible for newer C++ revisions to have defined safe APIs the committee has chosen not to, for better or worse. Take
std::span, for instance; it was introduced in C++20 so the committee is not bound by historical baggage and in principle could have chosen to make the easy-to-useoperator[]safe® by requiring bounds checks. But they didn’t; in fact, they voted to remove the checkedat()accessor (see the notes for revision 1), and it took until C++26 to add it back in!
score 2 · canonical
