I haven't really paid attention to C++ in a while (last neat feature I saw was concepts). Usually I kind of enjoy the weird, expressive code golf you can write with the new features. For example I tried out using concepts to ensure Matrix dimensions were compatible with the desired operation. Not really that useful, but was a neat thought experiment.<p>But these proposed contracts... I just don't really see much benefit it offers over just explicitly writing your own assertions.<p>I suppose you can see the requirements in the declarations, but I would wager that fancy modern C++ declarations quickly become so long and verbose that you'd get quickly fatigued from reading them. Perhaps in the LLM era they are more useful?
Stroustrup's list of objections sounds quite sensible to me: <a href="https://www.stroustrup.com/Contracts_fundamental_flaws.pdf" rel="nofollow">https://www.stroustrup.com/Contracts_fundamental_flaws.pdf</a>
Agreed. I've not liked the contract setup for various reasons. It seems like the proponents of it are mostly there because it scratches an itch in PL PhD studies rather than being an actually useful feature.<p>I remain unconvinced as why this is a necessary feature over the already existing `assert` statements.
At the end, it became very clear that some of the prose came from LLMs. It's a very short survey of other objections. I wonder why he/they reached for an LLM instead of writing it manually.
The paper sounds very, very much like Claude
This is a well-written and quite diplomatic summary by Herb Sutter. I'm not plugged in enough to be sure he's correct that Stroustrup's opposition was the main reason the C++26 vote failed where others have passed, but it sounds plausible.<p>It also lists 6 possible paths forward for C++26 with pros and cons. I think they are roughly:<p>A. Skip an official C++26 standard but roll these features into C++29.<p>B. Remove contracts from C++26 and pass it.<p>C. Say "please" and vote again.<p>D. Tweak contracts regarding virtual functions and vote again.<p>E. Pass C++26 as many different countries' <i>national</i> standards rather than an international standard.<p>F. Do E and also consider leaving ISO and moving to ANSI/INCITS instead.
As someone who spent a few weeks trying to get into C++ (pre-LLM, so maybe it's better now?) and kind of just gave up out of confusion, I really found it challenging to deal with the multiple different ways to do something. I mean, which pointer should I use? How should I iterate? I'm sure there's an answer to this, but I just found it easier to go back to python and hit up rust when I needed it.<p>It seems like part of the concern is that the feature needs to be shipped right the first time because it'll live forever. Perhaps allowing features to be deprecated and then removed could help?<p>But idk, I haven't written anything that lasted 36 years, so maybe the current approach is for the best!
C++ has to be approached as a grab bag of useful parts, not a coherent whole. It's somewhat akin to Python if Python modules were C++ language features. It's said that nobody knows all of C++, even its creators.<p>Do you know C? Everything that works in C works in C++, modulo a few minor syntactic changes (void* doesn't implicitly convert). You can get acquainted to C++ features on an as-needed basis. Other people will surely disagree with me on this.<p>With pointer types in particular, I'd say use C-style pointers long enough to know how frustrating they are but also how they work. Then you'll know what unique_ptr and shared_ptr are doing to help you. (And those are the only two you need 99% if the time. weak_ptr is a spin-off of shared_ptr that you'll know if you need it. observer_ptr is just stupid ivory-tower stuff.)
Does the existence of PEP 572 not similarly result in being paralyzed and overwhelmed at the number of ways to do something being greater than 1?<p>C++ definitely has complexity, but I'm surprised by the two examples specifically chosen. Which pointer? C++ only has 2: unique_ptr and shared_ptr (or 2.5 if you want to include weak_ptr but that necessarily means you already chose strong_ptr first). While this might be a complicated choice if you're just comparing to Python, but it shouldn't be if you're familiar with Rust since Rust has you make the same choice.<p>And then "how should I iterate" is similarly the same as any other language? Either a for or a for-each loop?<p>There's definitely baggage from 38 years of lessons learned, to be sure, just those examples are odd choices
You've already mentioned four pointer types which are separate to the original plain vanilla pointer concept.<p><a href="https://en.cppreference.com/cpp/language/pointer" rel="nofollow">https://en.cppreference.com/cpp/language/pointer</a><p>When I started C++ nearly 15 years ago this was already a huge problem. I bought some books and looked up online references and after a while I discovered that both were already out of date.<p>C++ is a chimera. And it hasn't improved since.<p>Other languages are far more stable, conceptually. New features rarely undermine or reinvent the older ones.<p>C++ is unique in the way it keeps modifying its own core. There is no formal deprecation or EOL process, so the language is more like an archaeological dig than a living standard.
> There is no formal deprecation or EOL process<p>Yes there is or we'd have been talking about std::auto_ptr as well.<p>> New features rarely undermine or reinvent the older ones.<p>Many languages have mistakes they've later tried to sweep under the rug. JavaScript arrow functions trying to convince everyone that bind(this) wasn't ever a thing, for example.<p>C++, for whatever reason, is consistently the only language where people are determined to always dig up the skeletons and pretend they are at all still relevant. It's not unique in having skeletons. It's not unique in having language spec mistakes that can't realistically be changed, just ammended with a newer, better alternative.<p>But like the saying goes, there are only two kinds of languages: the ones people complain about and the ones nobody uses
> And then "how should I iterate" is similarly the same as any other language? Either a for or a for-each loop?<p>for (ignoring do and while) and foreach loops, std::for_each, ranges for_each at the very least
...which is the same as nearly every other language? What language doesn't have that same collection of stuff?<p>Java: for, for-each, do-while with iterator, list.forEach(..), list.stream().forEach(..), etc...
Don't forget * (true pointer) and & (called a reference, more honestly a pointer that can't be repointed, except with UB it can _totally_ get repointed).
Knowing the story behind a decision like that is worth a thousand spec clauses.<p>When the language got operator overloading there was the question of how to convert the arguments from the operator itself to its implementation. You wanted to write a+b, not &a+&b but most versions of operator+ were going to want const pointers for efficiency. One option was to make the & implicit, another was to introduce a type of argument that didn't need & but acted like a pointer. So references were born.<p>I ignore people who advise to use references when something can't be null. A null reference is UB just like a null pointer, it just shifts where the UB is, but the compiler is likely to produce the same code either way and the program is just as likely to crash. Instead, I use pointers to make it obvious that something is a pointer, and references when it should be invisible, just like with operator overloading.
The source of the UB is dereferncing a null pointer. I don't think it's possible to obtain a null reference otherwise. I, on the other hand, ignore people who advise against using references for parameters that can't be nullable. Particularly const references, which provide potentially massive performance improvements, and are a core idiom in C++.
Made me LOL
Features are deprecated and removed. It's uncommon but it happens when a new feature makes all use of the old feature unnecessary. Usually it's deprecated for a few versions and then removed some years later. You can always compile old code against an old standard.<p>C++ does have many ways to do anything. Most of that is historical, or due to features and library facilities with overlapping capabilities, or inherent to multi-paradigm programming languages.<p>Every C++ project is written in its own language. Its own "house flavor." This is true of all languages, but is particularly stark for a language as old, complicated, and powerful as C++.
I share the same sentiment and experience from trying to get into C++. I bought "A Tour of C++" (2022, Stroustrup), I read it back to back, and I thought to myself "it's not so bad, seems quite nice actually, specially the modules part"... oh boy. After 2 weeks writing a HTTP requests CLI tool I was feeling really demotivated, I gave up and went to rust, for no special reason, I just had it queued up after reading "The Rust Programming Language" book.<p>> pre-LLM, so maybe it's better now?<p>I came back to the toy tool some time later and used GLM 4.x to implement it further. I had a good experience with it and I understood the code it generated. From my experience, in general, LLM is a good mentor, if you are willing to ingest the content step by step. For me, it is no different from when I used StackOverflow (and other forums) to try and ask how a thing is done.
I studies for some descent while with the objective of using unreal engine, then i found out everyone was just using blue prints so not even them liked CPP
> which pointer should I use?<p>The answer is almost always: no pointer
> As someone who spent a few weeks trying to get into C++ and kind of just gave up out of confusion<p>what should be conclude from this? this is like saying you tried to become a professional trapeze artist for a couple of days. C++ is complex and deep. what did you seriously hope to accomplish in a "few weeks"?<p>i started writing C++ professionally 4 years ago, i.e., i write it every single day at $JOB. there are still large parts of the language i'm not familiar with.
> what should be conclude from this? … C++ is complex and deep.<p>Yes, that's the point. Much of this complexity would not be necessary to achieve the same things, given the ability to redesign from scratch.
You're very diplomatic (yet downvoted), C++ is an abomination of a language. It just puts too much accidental cognitive load on developers. I understand that<p>- it shines for some tasks where Rust isn't capable enough<p>- it's impossible to get rid of because of all the code so we may as well modernize it<p>- there's an active community of people who built a career of being C++ experts and have no interest to see the language disappears.<p>Even in the LLM era, I find it's difficult to code in C++. The language has so many corner cases that LLMs make mistakes. My experience 6 months ago was that the code written by my agent had tons of complex linter warning (exceptions, lambda leaking references, futures whatever... that I just can't avoid because the codebase mixes all of this). Whenever possible, it's probably time to either modernize it or port it to Rust, component by component, until the C++ part is small or inexistant.
Maye you need a better AI. Just sayin'. My AI (Claude) doesn't make mistakes. Ok. It doesn't always think strategically, or factor well. But it certainly doesn't struggle with language mechanics.<p>Modern best practice for C++ is to crank the warnings to maximum, and to treat warnings as errors. You absolutely should treat linter-like warnings as an error, because they invariably are. So you know.
I think this is putting too much weight on <i>this</i> event and Bjarne’s position on contracts.<p>Bjarne’s opposition to contracts might have tipped the scale but there is definitely growing weariness (and wariness) about large half-baked features that are left as exercises to the implementors (looking at you modules).<p>Ignoring that historical context when analysing <i>this</i> vote seems like a mistake.
Just stopped by to say Sutter is not only right to be reiterating Bjarne's position here, he is also the author of IMO the most impactful paper regarding the progress of hardware performance and the shift to concurrent execution: <a href="https://www.cs.utexas.edu/~lin/cs380p/Free_Lunch.pdf" rel="nofollow">https://www.cs.utexas.edu/~lin/cs380p/Free_Lunch.pdf</a>
C++ committee members (mainly Boehm, but also Stroutstrup) also deserve a <i>lot</i> of credit for giving us a well specified memory model for concurrency outside of functional languages. Rust basically copy/pasted the C++11 memory model wholesale. Rust's fearless concurrency owes a lot to standing on the shoulders of giants.
C++11 borrowed heavily from the Java memory model that came before it.<p>It's basically everyone learning from everyone else on what works and what doesn't work.
I don't think fearless concurrency is strongly related to the atomic memory ordering model.<p>C++ 11 wants to model two non-trivial orderings, Acquire-Release and Consume-Release, but only Acquire-Release is actually usable. So yes, C and Rust both also choose to offer Acquire-Release ordering like C++ but it's also clear that the universe of possibilities is actually smaller than WG21 realised when C++ 11 was developed.<p>If on your first random stab you guess 65537 is prime, you're correct and you <i>might</i> foolishly assume all those Fermat Numbers are prime and then try say, 4294967297. Nope, that's divisible by 641 for some reason. There are only Five of these Fermat Primes so far as we know, they weren't common after all.
My understanding is that Consume is actually a desirable semantic that models what algorithms often want, and would allow for more optimization than Acquire if implemented properly, but that its failure was due almost entirely to it being difficult to implement correctly in compilers. In particular, the semantic is difficult to preserve across all compiler passes, especially given that existing compilers are essentially 20-40 years of accumulated optimizations that were never designed to preserve this semantic.<p>I sometimes wonder how difficult it would be for a compiler that was designed from the outset to implement Consume correctly.
Nit: Rust currently adheres to C++20 memory model. At least one notable difference from earlier versions which tripped me up recently is that release sequences aren't extended by further writes.
Languages have not yet evolved to multi-core properly IMO<p>I've sketched what I think an ideal "language of today" would look like, and one of the core tenets is making concurrent code explicit and part of the design surface.<p>You might imagine a webserver with main code like:<p><pre><code> spawn(on_core: 0) {
await receive_packet(input)
}
</code></pre>
Or iterating over "num_cpus" and doing the same thing, with hashing based on CPU number for shared-memory resources.
The problem is that when you build at this layer, you end up writing far more scheduling code than business logic. Eventually you've built the abstractions we already use today.<p>The vast, vast majority of software, and software developers, simply do not care about core affinity, NUMA, noisy neighbors, etc.<p>Even on systems or situations where this matters (massive number of cores and memory, multiple sockets, thousands of threads, etc.) the juice is rarely worth the squeeze.<p>Doubly so when you end up being I/O bound anyways.<p>Nearly every software developers wants their software to work across multiple deployment targets. Maybe you just target x86-64, and you don't care about the number of CPUs because you'll be upgrading your servers every couple of years.<p>No one wants to deal with difficult-to-reproduce issues related to scheduling in niche configurations. It's much more efficient for runtime or compiler engineers to solve these problems for us, leveraging the vast amount of resources of corporations to develop and test these features. Then you have a place your code can live happily ignorant of its runtime constraints.<p>And that, my friend, is the beauty of our industry.
I disagree. In a world of different architectures, out-of-order execution, asymmetric cores, branch prediction, per-core caching and information leakage between threads and the branch not taken, my opinion is firmly that most people shouldn't be writing multithreaded code because it is almost impossible to get it correct and safe.<p>The effects can be subtle and lurk for years until a seemingly unrelated change exposes them. It starts with optimizations like instead of spawning a thread, you use a thread pool, possibly abstracted as some kind of executor service. Other code might use different thread pools to isolate them because each pool has its own limits on the number of threads. Some might grow but usually they won't.<p>Until one day you end up with thread starvation and a deadlock through a series of benign seeming optimizations. There are threads around but not in the right pools and the entire thing grinds to a halt.<p>Cooperative async/await on a single thread is the ideal solution for most use cases, particularly with complete environment teardown between requests. Yes, this is basically the PHP model.
I’m not sure where you draw the conclusion that this was impactful. Intel had been sounding the alarm for at least a couple years at that point (longer if you were privy to private communication with them). Not to mention that C++ failed pretty miserably to enable safe concurrent and parallel programming for the masses, and the industry as a whole still struggles as a result.
That paper turned out to be enterily wrong, not only did single core performance keep increasing significantly, today most software is being written with almost no concurrency in mind
That's because concurrency is really in the SDK or stdlib for the language or platform.<p>In my opinion, that's really the best place for it. From what I understand, that's always been FORTRAN's secret sauce.<p>I've been doing threaded code since Day One (Machine Code), and have gotten it wrong, quite often. There are a million things that can cause concurrent code to actually perform <i>worse</i> than single-thread code.<p>These days, I usually just let the platform pick the threads, and generally write code that will play along.
The only software that is written without concurrency in mind is software that uses very few resources.<p>Anything else heavily uses concurrency in all kinds of ways.
which turns out to be the vast majority of software, because contrary to what the author of the paper predicted, single thread performance ended up being more than good enough (and not the bottleneck)
36 years of sustained agreement is not good; it means nobody is saying no to anything, and you have a giant, dirty snowball rolling down a mountain.
I'm amazed. Someone somewhere conceived of a feature, and it wasn't put into C++!
Before reading it i was sure this was going to be about how the PHP creator warned everyone hes language was for blogging, then we had an apocalipse
Not only did Kernighan and Ritchie not update the C book for a standard newer than C89, Kernighan underscored that by updating the <i>The Awk Book</i> in 2023 (with Aho and Weinberger: the other two letters in Awk). As if to say C is so far gone that even Awk is more worth writing about.
I'm sure I'm not the only one but I feel like C++ is over/done. I work in a C++ code base and have been doing C++ for > 20 years and to me at least, the writing is on the wall.<p>1. C++ is unsafe. Bjarne keeps giving talks that effectively say "use it perfectly and don't make any mistakes and it's safe". That's not useful anymore. In a world where LLMs can find every mistake to exploit it, all the C++ projects are struggling to keep up and keeping them in C++ is basically guaranteeing there will be more exploits in the future. Yes, not every bug gets solved by switching to a safer language but reducing the attack surface in the new reality is no longer something that can be put off.<p>2. LLMs can re-write. The previous arguments used to be "there are C++ legacy code bases so C++ will never die". That was a valid argument before an LLM would just rewrite it all. I get people will say they aren't perfect, neither are humans though. Further they get better all the time, and we see their progress. People pointing an LLM at some project and it magically making it work on some other platform etc... It's clear it's happening.
I recoil when I look at the roadmap for C++2x (most of it, anyway), it's a bloated monster and I suspect the language is on its last legs.<p>It will probably take a lot longer to die than I expect, though, too many legacy third party libraries are written in C++ and most people don't really like the syntax for the logical alternative (Rust).
As obvious as they sound i havent tought about those points before, they do make sense
“Owners”? No one owns a language. People will do what they find pragmatic, practical and necessary. A committee who thinks they own a programming language has become pretentious and misunderstands their relationship with developers.
[flagged]
imagine you run into ub in your C program. Suddenly every single loc of your project, including dependencies, are in scope.<p>With Rust, only the unsafe blocks are in scope. The point of Rust is not to eliminate unsafe code, but to make it not scale with the size of your program. There is no good reason for those to be coupled.
I tend to wonder, in an alternate reality, where the buck stopped at C for systems languages, would we be better off because more people would use it and know how to fix memory related issues?<p>We’ve sent rockets into space running C/C++ and assembly. And that was long before C++11 rolled around.<p>I’m wondering because: man Rust is an ugly language. C is not perfect either, but it’s still pretty close to what it was FIFTY YEARS AGO.<p>Fast forward to now, and all we’ve come to is Rust? I just wonder why don’t we just keep using C, C#, and Java? I mean the former two are memory safe, and they can do “unsafe” just like Rust.<p>Another thing: why do we write pseudocode and not design languages to look like pseudocode? With Rust and modern C++ compile times you’d think the machine is parsing prose and turning it into assembly…
over-obsession with the language's aesthetics ignore that the type system / borrow checker / ... are good and legitimately assist with development and did so even before the widespread use of LLMs.<p>many of those who ended up as Rust developers came from C and C++, seeing improvement from what they were using.<p>languages have been designed more and more to allow for higher level behaviours, in Rust's case, although not pursuing psuedocode, generally allows for very high level abstractions. regardless the computer still has to run things, languages are typically deterministic in their handling and psuedocode leaves much undetermined, unfully specified.
> I tend to wonder, in an alternate reality, where the buck stopped at C for systems languages, would we be better off because more people would use it and know how to fix memory related issues?<p>I think that period has arguably already come and gone. C and C++ have been the most popular systems languages for <i>decades</i>. I think it's clear that mere popularity is not sufficient to produce memory-safe software with those languages. If anything, those languages are experiencing somewhat of a renaissance these days and it's these more recent developments that end up producing improvements with regard to memory safety.<p>> I just wonder why don’t we just keep using C, C#, and Java? I mean the former two are memory safe, and they can do “unsafe” just like Rust.<p>Bit of a nitpick, but I think you meant "latter" instead of "former".<p>That aside, there's more to picking a language to use than just memory safety and the ability to perform "unsafe" operations. Take your pick from more technical reasons (zero-overhead FFI to C, low-level control over data structures/operations/runtime, etc.) and more "soft" reasons (language affordances/features, ecosystem, etc.).<p>> Another thing: why do we write pseudocode and not design languages to look like pseudocode?<p>Perhaps COBOL might be what you're looking for? :P<p>As with many things, language design is full of tradeoffs, particularly when you want to produce something that is not too unfamiliar to a certain audience. IIRC another factor is that languages that want to work well for "experts" will tend to favor different points on the design spectrum. Consider APL-family languages - those are completely incomprehensible to anyone not familiar with them, but that same terseness is <i>highly</i> valued by proficient practitioners and is something pseudocode-like (or even "regular" code-like!) could never replicate.
All fair points, and yeah I meant latter. :)<p>It’s just interesting that ALL of these languages have a C compatible interface built into the language. That’s an interesting fact and says something about C that no other language can really claim. Nor will any other language claim that any time soon.<p>I’m not looking for anything in particular. It’s just that I’ve seen and used a lot of languages over the years, and I’m not wholly sold on the “memory safety” of Rust when something like Python can just put the performance sensitive parts in C and have safety for everything else.<p>Most languages are memory safe.
> and I’m not wholly sold on the “memory safety” of Rust when something like Python can just put the performance sensitive parts in C and have safety for everything else.<p>> Most languages are memory safe.<p>As I said, there's more to Rust than just memory safety. If memory safety were the <i>only</i> thing Rust offered then I think it wouldn't have garnered nearly as much attention as it has. It's memory safety <i>on top of everything else</i> that makes it as interesting as it is.