I am somewhat dismayed that contracts were accepted. It feels like piling on ever more complexity to a language which has already surpassed its complexity budget, and given that the feature comes with its own set of footguns I'm not sure that it is justified.
Here's a quote from Bjarne,
> So go back about one year, and we could vote about it before it got into the standard, and some of us voted no. Now we have a much harder problem. This is part of the standard proposal. Do we vote against the standard because there is a feature we think is bad? Because I think this one is bad. And that is a much harder problem. People vote yes because they think: "Oh we are getting a lot of good things out of this.", and they are right. We are also getting a lot of complexity and a lot of bad things. And this proposal, in my opinion is bloated committee design and also incomplete.
Just because Bjarne thinks the feature is bad doesnt mean it is bad. He can be wrong. The point is, most peoppe disagree with him, and so a lot of peoppe do think it is good.
There have been several talks about contracts and the somewhat hidden complexities in them. C++ contracts are not like what you'd initally expect. Compiler switches can totally alter how contracts behave from getting omitted to reporting failures to aborting the program. There is also an optional global callback for when a contract check fails.
Different TUs can be compiled with different settings for the contract behavior. But can they be binary compatible? In general, no. If a function is declared in-line in a header, the comoiler may have generated two different versions with different contract behaviors, which violates ODR.
What happens if the contract check calls a helper function that throws an exception?
The whole things is really, really complex and I don't assume that I understand it properly. But I can see that there are some valid concerns against the feature as standardized and that makes me side with the opposition here: this was not baked enough yet
I can’t speak to the C++ contract design — it’s possible bad choices were made. But contracts in general are absolutely exactly what C++ needs for the next step of its evolution. Programming languages used for correct-by-design software (Ada, C++, Rust) need to enable deep integration with proof assistants to allow showing arbitrary properties statically instead of via testing, and contracts are /the/ key part of that — see e.g. Ada Spark.
C++ is the last language I'd add to any list of languages used for correct-by-design - it's underspecified in terms of semantics with huge areas of UB and IB. Given its vast complexity - at every level from the pre-processor to template meta-programming and concepts, I simply can't imagine any formal denotational definition of the language ever being developed. And without a formal semantics for the language, you cannot even start to think about proof of correctness.
As with Spark, proving properties over a subset of the language is sufficient. Code is written to be verified; we won’t be verifying interesting properties of large chunks of legacy code in my career span. The C (near-) subset of C++ is (modulo standard libraries) a starting point for this; just adding on templates for type system power (and not for other exotic uses) goes a long way.
The people who did contracts are aware of ada/spark and some have experience using it. Only time will tell if it works in c++ but they at least did all they could to give it a chance.
Note that this is not the end of contrats. This is a minimun viable start that they intend to add to but the missing parts are more complex.
Might be the case that Ada folks successfully got a bad version of contracts not amenable for compile-time checking into C++, to undermine the competition. Time might tell.
Ada used to be mandated in the US defense industry, but lots of developers and companies preferred C++ and other languages, and for a variety of reasons, the mandate ended, and Ada faded from the spotlight.
On their own merits, people choose SMS-based 2FA, "2FA" which lets you into an account without a password, perf-critical CLI tools written in Python, externalizing the cost of hacks to random people who aren't even your own customers, eating an extra 100 calories per day, and a whole host of other problematic behaviors.
Maybe Ada's bad, but programmer preference isn't a strong enough argument. It's just as likely that newer software is buggier and more unsafe or that this otherwise isn't an apples-to-apples comparison.
Right, I think the tension here is that we would like contracts to exist in the language, but the current design isn't what it needs to be, and once it's standardized, it's extremely hard to fix.
The devil is in the details, because standardization work is all about details.
From my outside vantage point, there seems to be a few different camps about what is desired for contracts to even be. The conflict between those groups is why this feature has been contentious for... a decade now?
Some of the pushback against this form of contracts is from people who desire contracts, but don't think that this design is the one that they want.
But why? You can do everything contracts do in your own code, yes? Why make it a language feature? I'm not against growing the language, but I don't see the necessity of this specific feature having new syntax.
Pre- and postconditions are actually part of the function signature, i.e. they are visible to the caller. For example, static analyzers could detect contract violations just by looking at the callsite, without needing access to the actual function implementation. The pre- and postconditions can also be shown in IDE tooltips. You can't do this with your own contracts implementation.
Finally, it certainly helps to have a standardized mechanisms instead of everyone rolling their own, especially with multiple libraries.
Contracts are about specifying static properties of the system, not dynamic properties. Features like assert /check/ (if enabled) static properties, at runtime. static_assert comes closer, but it’s still an awkward way of expressing Hoare triples; and the main property I’m looking for is the ability to easily extract and consider Hoare triples from build-time tooling. There are hacky ways to do this today, but they’re not unique hacky ways, so they don’t compose across different tools and across code written to different hacks.
The common argument for a language feature is for standardization of how you express invariants and pre/post conditions so that tools (mostly static tooling and optimizers) can be designed around them.
But like modules and concepts the committee has opted for staggered implementation. What we have now is effectively syntax sugar over what could already be done with asserts, well designed types and exceptions.
C++ contracts standardizes what people already do in C++. Where is the complexity in that? It removes the need to write your own implementation because the language provides a standard interoperable one.
An argument can be made that C++26 features like reflection add complexity but I don't follow that argument for contracts.
Has any project ever tried to quantify a “complexity budget” and stick to it?
I’m fascinated by the concept of deciding how much complexity (to a human) a feature has. And then the political process of deciding what to remove when everyone agrees something new needs to be accepted.
Can you share what aspects of the design you (and Stroustroup) aren't happy with? Stroustroup has a tendency of being proven right, with 1-3 decade lag.
Without a significant amount of needed context that quote just sounds like some awkward rambling.
Also almost every feature added to C++ adds a great deal of complexity, everything from modules, concepts, ranges, coroutines... I mean it's been 6 years since these have been standardized and all the main compilers still have major issues in terms of bugs and quality of implementation issues.
I can hardly think of any major feature added to the language that didn't introduce a great deal of footguns, unintended consequences, significant compilation performance issues... to single out contracts is unusual to say the least.
This is awesome. I've was a dev on the C++ team at MS in the 90s and was sure that RTTI was the closest the language would ever get to having a true reflection system.
The best thing the C++ WG could do is to spend an entire release cycle working on modules and packaging.
It's nice to have new features, but what is really killing C++ is Cargo. I don't think a new generation of developers are going to be inspired to learn a language where you can't simply `cargo add` whatever you need and instead have to go through hell to use a dependency.
To me, the most important feature of Cargo isn't even the dependency management but that I don't ever need to tell it which files to compile or where to find them. The fact that it knows to look for lib.rs or main.rs in src and then recursively find all my other modules without me needing to specify targets or anything like that is a killer feature on its own IMO. Over the past couple of years I've tried to clone and build a number of dotnet packages for various things, but for an ecosystem that's supposedly cross-platform, almost none of them seem to just work by default when I run `dotnet build` and instead require at least some fixes in the various project files. I don't think I've ever had an issue with a Rust project, and it's hard not to feel like a big part of that is because there's not really much configuration to be done. The list of dependencies is just about the only thing in there that effects the default build; if there's any other configuration other than that and the basic metadata like the name, the repo link, the license, etc., it almost always will end up being specifically for alternate builds (like extra options for release builds, alternate features that can be compiled in, etc.).
> The fact that it knows to look for lib.rs or main.rs in src and then recursively find all my other modules without me needing to specify targets or anything like that is a killer feature on its own IMO.
In the interest of pedantry, locating source files relative to the crate root is a language-level Rust feature, not something specific to Cargo. You can pass any single Rust source file directly to rustc (bypassing Cargo altogether) and it will treat it as a crate root and locate additional files as needed based on the normal lookup rules.
I suffered with Java from
Any, Maven and Gradle (the oldest is the the best). After reading about GNU Autotools I was wondering why the C/C++ folks still suffer? Right at that time Meson appeared and I skipped the suffering.
* No XML
* Simple to read and understand
* Simple to manage dependencies
* Simple to use options
Meson is indeed nice, but has very poor support for GPU compilation compared to CMake. I've had a lot of success adopting the practices described in this talk, https://www.youtube.com/watch?v=K5Kg8TOTKjU. I thought I knew a lot of CMake, but file sets definitely make things a
lot simpler.
Meson merges the crappy state of C/C++ tooling with something like Cargo in the worst way possible: by forcing you to handle the complexity of both. Nothing about Meson is simple, unless you're using it in Rust, in which case you're better off with Cargo.
In C++ you don't get lockfiles, you don't get automatic dependency install, you don't get local dependencies, there's no package registry, no version support, no dependency-wide feature flags (this is an incoherent mess in Meson), no notion of workspaces, etc.
Compared to Cargo, Meson isn't even in the same galaxy. And even compared to CMake, Meson is yet another incompatible incremental "improvement" that offers basically nothing other than cute syntax (which in an era when AI writes all of your build system anyway, doesn't even matter). I'd much rather just pick CMake and move on.
Build system generators (like Meson, autotools, CMake or any other one) can't solve programming language module and packaging problems, even in principle. So, it's not clear what your argument is here.
> I’m still surprised how people ignore Meson. Please test it :)
I did just that a few years ago and found it rather inconvenient and inflexible, so I went back to ignoring it. But YMMV I suppose.
Agreed, arcane cmake configs and or bash build scripts are genuinely off-putting. Also cpp "equivalents" of cargo which afaik are conan and vcpkg are not default and required much more configuring in comparison with cargo. Atleast this was my experience few years ago.
It's fundamentally different; Rust entirely rejects the notion of a stable ABI, and simply builds everything from source.
C and C++ are usually stuck in that antiquated thinking that you should build a module, package it into some libraries, install/export the library binaries and associated assets, then import those in other projects. That makes everything slow, inefficient, and widely dangerous.
There are of course good ways of building C++, but those are the exception rather than the standard.
"Stable ABI" is a joke in C++ because you can't keep ABI and change the implementation of a templated function, which blocks improvements to the standard library.
In C, ABI = API because the declaration of a function contains the name and arguments, which is all the info needed to use it. You can swap out the definition without affecting callers.
That's why Rust allows a stable C-style ABI; the definition of a function declared in C doesn't have to be in C!
But in a C++-style templated function, the caller needs access to the definition to do template substitution. If you change the definition, you need to recompile calling code i.e. ABI breakage.
If you don't recompile calling code and link with other libraries that are using the new definition, you'll violate the one-definition rule (ODR).
This is bad because duplicate template functions are pruned at link-time for size reasons. So it's a mystery as to what definition you'll get. Your code will break in mysterious ways.
This means the C++ committee can never change the implementation of a standardized templated class or function. The only time they did was a minor optimization to std::string in 2011 and it was such a catastrophe they never did it again.
That is why Rust will not support stable ABIs for any of its features relying on generic types. It is impossible to keep the ABI stable and optimize an implementation.
> C and C++ are usually stuck in that antiquated thinking that you should build a module, package it into some libraries, install/export the library binaries and associated assets, then import those in other projects. That makes everything slow, inefficient, and widely dangerous.
It seems to me the "convenient" options are the dangerous ones.
The traditional method is for third party code to have a stable API. Newer versions add functions or fix bugs but existing functions continue to work as before. API mistakes get deprecated and alternatives offered but newly-deprecated functions remain available for 10+ years. With the result that you can link all applications against any sufficiently recent version of the library, e.g. the latest stable release, which can then be installed via the system package manager and have a manageable maintenance burden because only one version needs to be maintained.
Language package managers have a tendency to facilitate breaking changes. You "don't have to worry" about removing functions without deprecating them because anyone can just pull in the older version of the code. Except the older version is no longer maintained.
Then you're using a version of the code from a few years ago because you didn't need any of the newer features and it hadn't had any problems, until it picks up a CVE. Suddenly you have vulnerable code running in production but fixing it isn't just a matter of "apt upgrade" because no one else is going to patch the version only you were using, and the current version has several breaking changes so you can't switch to it until you integrate them into your code.
It's not true that Rust rejects "the notion of a stable ABI". Rust rejects the C++ solution of freeze everything and hope because it's a disaster, it's less stable than some customers hoped and yet it's frozen in practice so it disappoints others. Rust says an ABI should be a promise by a developer, the way its existing C ABI is, that you can explicitly make or not make.
Rust is interested in having a properly thought out ABI that's nicer than the C ABI which it supports today. It'd be nice to have say, ABI for slices for example. But "freeze everything and hope" isn't that, it means every user of your language into the unforeseeable future has to pay for every mistake made by the language designers, and that's already a sizeable price for C++ to pay, "ABI: Now or never" spells some of that out and we don't want to join them.
"That makes everything slow, inefficient, and widely dangerous."
There nothing faster and more efficient than building C programs. I also not sure what is dangerous in having libraries. C++ is quite different though.
Of course there is. Raw machine code is the gold standard, and everything else is an attempt to achieve _something_ at the cost of performance, C included, and that's even when considering whole-program optimization and ignoring the overhead introduced by libraries. Other languages with better semantics frequently outperform C (slightly) because the compiler is able to assume more things about the data and instructions being manipulated, generating tighter optimizations.
In my experience, no one does build systems right; Cargo included.
The standard was initially meant to standardize existing practice. There is no good existing practice. Very large institutions depending heavily on C++ systematically fail to manage the build properly despite large amounts of third party licenses and dedicated build teams.
With AI, how you build and integrate together fragmented code bases is even more important, but someone has yet to design a real industry-wide solution.
Speedy convenience beats absolute correctness anyday. Humans are not immortal and have finite amount of time for life and work. If convenience didn't matter, we would all still be coding in assembly or toggling hardware switches.
C++ builds are extremely slow because they are not correct.
I'm doing a migration of a large codebase from local builds to remote execution and I constantly have bugs with mystery shared library dependencies implicitly pulled from the environment.
This is extremely tricky because if you run an executable without its shared library, you get "file not found" with no explanation. Even AI doesn't understand this error.
No, because most major compilers don't support header units, much less standard library header units from C++26.
What'll spur adoption is cmake adopting Clang's two-step compilation model that increases performance.
At that point every project will migrate overnight for the huge build time impact since it'll avoid redundant preprocessing. Right now, the loss of parallelism ruins adoption too much.
The idea is great, the execution is terrible. In JS, modules were instantly popular because they were easy to use, added a lot of benefit, and support in browsers and the ecoysystem was fairly good after a couple of years. In C++, support is still bad, 6 years after they were introduced.
No idea if modules themselves are failed or no, but if c++ wants to keep fighting for developer mindshare, it must make something resembling modules work and figure out package management.
yes you have CPM, vcpkg and conan, but those are not really standard and there is friction involved in getting it work.
Much like contracts--yes, C++ needs something modules-like, but the actual design as standardized is not usable.
Once big companies like Google started pulling out of the committee, they lost their connection to reality and now they're standardizing things that either can't be implemented or no one wants as specced.
I emphatically agree. C++ needs a standard build system that doesn’t suck ass. Most people would agree it needs a package manager although I think that is actually debatable.
Neither of those things require modules as currently defined.
I'm not the PC but I think you miss most of the pain points due to: 'personal' projects.
There's not a compatible format between different compilers, or even different versions of the same compiler, or even the same versions of the same compiler with different flags.
This seems immediately to create too many permutations of builds for them to be distributable artifacts as we'd use them in other languages. More like a glorified object file cache. So what problem does it even solve?
Because as a percentage of global C++ builds they’re used in probably 0.0001% of builds with no line of sight to that improving.
They have effectively zero use outside of hobby projects. I don’t know that any open source C++ library I have ever interacted with even pretends that modules exist.
"Failed idea" gives modules too much credit. Outside old codebases, almost no one outside C++ diehards have the patience for the build and tooling circuss they create, and if you need fast iteration plus sane integration with existing deps, modules are like trading your shoes for roller skates in a gravel lot. Adopting them now feels like volunteering to do tax forms in assembbly.
I frankly wish we'd stop developing C++. It's so hard to keep track of all the new unnecessary toys they're adding to it. I thought I knew C++ until I read some recent C++ code. That's how bad it is.
Meanwhile C++ build system is an abomination. Header files should be unnecessary.
> Second, conforming compiler and standard library implementations are coming quickly. Throughout the development of C++26, at any given point both GCC and Clang had already implemented two-thirds of C++26 features. Today, GCC already has reflection and contracts merged in trunk, awaiting release.
std::execution is very interesting, but will be difficult to get started with, as cautioned by Sutter. This HPC Wire article demonstrates how to use standard C++ to benefit from asynchronously parallel computation on both CUDA and MPI:
Overlapping communication and computation has been a common technique for decades in high-performance computing to "hide latency", which leads to better scaling. Now standard C++ can be used to express parallel algorithms without tying to a specific scheduler.
Finally, reflection has arrived, five years after I last touched a line in c++. I wonder how long would it take the committee, if ever, to introduce destructing move.
If you ask me (and why wouldn't you? :-)...) I really wish the C++ WG would do several things:
1. Standardize a `restrict` keyword and semantics for it (tricky for struct/class fields, but should be done).
2. Uniform Function Call Syntax! That is, make the syntax `obj.f(arg)` mean simply `f(obj, arg)` . That would make my life much easier, both as a user of classes and as their author. In my library authoring work particularly. And while we're at it, let us us a class' name as a namespace for static methods, so that Obj::f the static method is simply the method f in namespace Obj.
3. Get compiler makers to have an ABI break, so that we can do things like passing wrapped values in registers rather than going through memory. See: https://stackoverflow.com/q/58339165/1593077
4. Get rid of the current allocators in the standard library, which are type-specific (ridiculous) and return pointers rather than regions of memory. And speaking of memory regions (i.e. with address and size but no element type) - that should be standardized too.
What are you talking about, there is actually too much unicode awareness in C++. Unicode is not the same thing as utf-8. And, frankly, no language does it right, I'm not even sure "right" exists with Unicode
c++20's u8strings took a giant steaming dump on a number of existing projects, to the point that compiler flags had to be introduced to disable the feature just so c++20 would work with existing codebases. Granted that's utf-8 (not the same thing as unicode, as mentioned) but it's there.
No such strategy is necessary. That discourse was about not using C++ for applications where Java would work just as well.
The US government still uses C++ widely for new projects. For some types of applications it is actually the language of choice and will remain so for the foreseeable future.
>"For some types of applications it is actually the language of choice..."
Can you give an example please? And how does it correspond to government ONCD report and other government docs "recommending" "safe" languages like: Rust (noted for its ability to prevent memory-unsafe code), Go, Java, Swift, C#, Ruby, Ada
Among other things I design and implement high performance C++ backends. for some I got SOCS2 Type II certification but I am curious about future. Do not give a flying fuck about what the criteria for military projects as I would not ever touch one even if given a chance.
I watched a talk from Bjarne Stroustrup at CppCon about safety and it was pretty second hand embarrassing watching him try to pretend C++ has always been safe and safety mattered all along to them before Rust came along.
Well, there has been a long campaign against manual memory management - well before Rust was a thing. And along with that, a push for less use of raw pointers, less index loops etc. - all measures which, when adopted, reduce memory safety hazards significantly. Following the Core Guideliness also helps, as does using span's. Compiler warnings has improved, as has static analysis, also in a long process preceding Rust.
Of course, this is not completely guaranteed safety - but safety has certainly mattered.
Contracts feel like the right direction but the wrong execution timeline. The Ada/SPARK model shows how powerful contracts become when they feed into static verification — but that took decades of iteration on a language with far cleaner semantics. Bolting that onto C++ where UB is load-bearing infrastructure is a different beast entirely.
The real risk isn't complexity for complexity's sake — it's that a "minimum viable" contracts spec gets locked in, and then the things that would actually make it useful for proof assistants become impossible to retrofit because they'd break the v1 semantics. Bjarne's concern about "incomplete" is more worrying to me than "bloated."
Seeing that pic at the top of the article, and reflecting on my own experiences with rust: It is wild just how male-centric systems programming languages are. I'm from a career backround that's traditionally male-dominated (military aviation), but the balance is far more skewed among C, C++ and Rust developers.
Please don't use Hacker News as a religious or ideological battleground. It tramples curiosity. Please don't pick the most religiously/ideologically provocative thing in an article or post to complain about in the thread. Find something interesting to respond to instead.
This is interesting! People ignoring this, I think, is also interesting on its own. I respect if other people disagree, but that's my 2c. I think our overton windows may not agree here, but I think this is part of the value of discussions with other humans.
Are you a moderator? The directive tone of this post is as if from an authority figure, but, but I do not believe you are one.
I do not believe there is anything about a religious or ideological background here. Could you please clarify?
I also believe it is your post that could be more accurately described as trampling curiosity; I believe there is a role reversal, in that I think your comment is a better description for trampling curiosity than the post your are responding. I'm not trying to be snarky - I'm curious how you came to those conclusions.
I am somewhat dismayed that contracts were accepted. It feels like piling on ever more complexity to a language which has already surpassed its complexity budget, and given that the feature comes with its own set of footguns I'm not sure that it is justified.
Here's a quote from Bjarne,
> So go back about one year, and we could vote about it before it got into the standard, and some of us voted no. Now we have a much harder problem. This is part of the standard proposal. Do we vote against the standard because there is a feature we think is bad? Because I think this one is bad. And that is a much harder problem. People vote yes because they think: "Oh we are getting a lot of good things out of this.", and they are right. We are also getting a lot of complexity and a lot of bad things. And this proposal, in my opinion is bloated committee design and also incomplete.
Just because Bjarne thinks the feature is bad doesnt mean it is bad. He can be wrong. The point is, most peoppe disagree with him, and so a lot of peoppe do think it is good.
There have been several talks about contracts and the somewhat hidden complexities in them. C++ contracts are not like what you'd initally expect. Compiler switches can totally alter how contracts behave from getting omitted to reporting failures to aborting the program. There is also an optional global callback for when a contract check fails.
Different TUs can be compiled with different settings for the contract behavior. But can they be binary compatible? In general, no. If a function is declared in-line in a header, the comoiler may have generated two different versions with different contract behaviors, which violates ODR.
What happens if the contract check calls a helper function that throws an exception?
The whole things is really, really complex and I don't assume that I understand it properly. But I can see that there are some valid concerns against the feature as standardized and that makes me side with the opposition here: this was not baked enough yet
Coroutines went through the same cycle. Standardized in C++20, and I still hit compiler-specific differences in how symmetric transfer gets lowered.
I can’t speak to the C++ contract design — it’s possible bad choices were made. But contracts in general are absolutely exactly what C++ needs for the next step of its evolution. Programming languages used for correct-by-design software (Ada, C++, Rust) need to enable deep integration with proof assistants to allow showing arbitrary properties statically instead of via testing, and contracts are /the/ key part of that — see e.g. Ada Spark.
C++ is the last language I'd add to any list of languages used for correct-by-design - it's underspecified in terms of semantics with huge areas of UB and IB. Given its vast complexity - at every level from the pre-processor to template meta-programming and concepts, I simply can't imagine any formal denotational definition of the language ever being developed. And without a formal semantics for the language, you cannot even start to think about proof of correctness.
As with Spark, proving properties over a subset of the language is sufficient. Code is written to be verified; we won’t be verifying interesting properties of large chunks of legacy code in my career span. The C (near-) subset of C++ is (modulo standard libraries) a starting point for this; just adding on templates for type system power (and not for other exotic uses) goes a long way.
The people who did contracts are aware of ada/spark and some have experience using it. Only time will tell if it works in c++ but they at least did all they could to give it a chance.
Note that this is not the end of contrats. This is a minimun viable start that they intend to add to but the missing parts are more complex.
Might be the case that Ada folks successfully got a bad version of contracts not amenable for compile-time checking into C++, to undermine the competition. Time might tell.
I strongly doubt that C++ is what's standing in the way of Ada being popular.
Ada used to be mandated in the US defense industry, but lots of developers and companies preferred C++ and other languages, and for a variety of reasons, the mandate ended, and Ada faded from the spotlight.
>the mandate ended, and Ada faded from the spotlight
Exactly. People stopped using Ada as soon as they were no longer forced to use it.
In other words on its own merits people don't choose it.
On their own merits, people choose SMS-based 2FA, "2FA" which lets you into an account without a password, perf-critical CLI tools written in Python, externalizing the cost of hacks to random people who aren't even your own customers, eating an extra 100 calories per day, and a whole host of other problematic behaviors.
Maybe Ada's bad, but programmer preference isn't a strong enough argument. It's just as likely that newer software is buggier and more unsafe or that this otherwise isn't an apples-to-apples comparison.
This is some pretty major conspiracy thinking, and would need some serious evidence. Do you have any?
> Programming languages used for correct-by-design software (Ada, C++, Rust) ...
A shoutout to Eiffel, the first "modern" (circa 1985) language to incorporate Design by Contract. Well done Bertrand Meyer!
Right, I think the tension here is that we would like contracts to exist in the language, but the current design isn't what it needs to be, and once it's standardized, it's extremely hard to fix.
The devil is in the details, because standardization work is all about details.
From my outside vantage point, there seems to be a few different camps about what is desired for contracts to even be. The conflict between those groups is why this feature has been contentious for... a decade now?
Some of the pushback against this form of contracts is from people who desire contracts, but don't think that this design is the one that they want.
But why? You can do everything contracts do in your own code, yes? Why make it a language feature? I'm not against growing the language, but I don't see the necessity of this specific feature having new syntax.
Pre- and postconditions are actually part of the function signature, i.e. they are visible to the caller. For example, static analyzers could detect contract violations just by looking at the callsite, without needing access to the actual function implementation. The pre- and postconditions can also be shown in IDE tooltips. You can't do this with your own contracts implementation.
Finally, it certainly helps to have a standardized mechanisms instead of everyone rolling their own, especially with multiple libraries.
Contracts are about specifying static properties of the system, not dynamic properties. Features like assert /check/ (if enabled) static properties, at runtime. static_assert comes closer, but it’s still an awkward way of expressing Hoare triples; and the main property I’m looking for is the ability to easily extract and consider Hoare triples from build-time tooling. There are hacky ways to do this today, but they’re not unique hacky ways, so they don’t compose across different tools and across code written to different hacks.
The common argument for a language feature is for standardization of how you express invariants and pre/post conditions so that tools (mostly static tooling and optimizers) can be designed around them.
But like modules and concepts the committee has opted for staggered implementation. What we have now is effectively syntax sugar over what could already be done with asserts, well designed types and exceptions.
DYI contracts don't compose when mixing code using different DYI implementations. Some aspects of contracts have global semantics.
C++ contracts standardizes what people already do in C++. Where is the complexity in that? It removes the need to write your own implementation because the language provides a standard interoperable one.
An argument can be made that C++26 features like reflection add complexity but I don't follow that argument for contracts.
Has any project ever tried to quantify a “complexity budget” and stick to it?
I’m fascinated by the concept of deciding how much complexity (to a human) a feature has. And then the political process of deciding what to remove when everyone agrees something new needs to be accepted.
Source of quote:
https://youtu.be/tzXu5KZGMJk?t=3160
Can you share what aspects of the design you (and Stroustroup) aren't happy with? Stroustroup has a tendency of being proven right, with 1-3 decade lag.
Well thats not always true. Initializer list is a glaring example. So are integer promotion some other things like
I mean... it's C++. The complexity budget is like the US government's debt ceiling.
Without a significant amount of needed context that quote just sounds like some awkward rambling.
Also almost every feature added to C++ adds a great deal of complexity, everything from modules, concepts, ranges, coroutines... I mean it's been 6 years since these have been standardized and all the main compilers still have major issues in terms of bugs and quality of implementation issues.
I can hardly think of any major feature added to the language that didn't introduce a great deal of footguns, unintended consequences, significant compilation performance issues... to single out contracts is unusual to say the least.
The "erroneous behavior" redefinition for reads of uninitialized variables is really interesting: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p27...
It does have a runtime cost. There's an attribute to force undefined behavior on read again and avoid the cost:
This is awesome. I've was a dev on the C++ team at MS in the 90s and was sure that RTTI was the closest the language would ever get to having a true reflection system.
Biggest open question is whether the small changes to the module system in this standard will actually lead to more widespread adoption
The best thing the C++ WG could do is to spend an entire release cycle working on modules and packaging.
It's nice to have new features, but what is really killing C++ is Cargo. I don't think a new generation of developers are going to be inspired to learn a language where you can't simply `cargo add` whatever you need and instead have to go through hell to use a dependency.
To me, the most important feature of Cargo isn't even the dependency management but that I don't ever need to tell it which files to compile or where to find them. The fact that it knows to look for lib.rs or main.rs in src and then recursively find all my other modules without me needing to specify targets or anything like that is a killer feature on its own IMO. Over the past couple of years I've tried to clone and build a number of dotnet packages for various things, but for an ecosystem that's supposedly cross-platform, almost none of them seem to just work by default when I run `dotnet build` and instead require at least some fixes in the various project files. I don't think I've ever had an issue with a Rust project, and it's hard not to feel like a big part of that is because there's not really much configuration to be done. The list of dependencies is just about the only thing in there that effects the default build; if there's any other configuration other than that and the basic metadata like the name, the repo link, the license, etc., it almost always will end up being specifically for alternate builds (like extra options for release builds, alternate features that can be compiled in, etc.).
> The fact that it knows to look for lib.rs or main.rs in src and then recursively find all my other modules without me needing to specify targets or anything like that is a killer feature on its own IMO.
In the interest of pedantry, locating source files relative to the crate root is a language-level Rust feature, not something specific to Cargo. You can pass any single Rust source file directly to rustc (bypassing Cargo altogether) and it will treat it as a crate root and locate additional files as needed based on the normal lookup rules.
For me the lack of dependency hell until I hit a c/c++ component somewhere in the build is the real winner.
I’m still surprised how people ignore Meson. Please test it :)
https://mesonbuild.com/
And Mesons awesome dependency handling:
https://mesonbuild.com/Dependencies.html
https://mesonbuild.com/Using-the-WrapDB.html#using-the-wrapd...
https://nibblestew.blogspot.com/2026/02/c-and-c-dependencies...
I suffered with Java from Any, Maven and Gradle (the oldest is the the best). After reading about GNU Autotools I was wondering why the C/C++ folks still suffer? Right at that time Meson appeared and I skipped the suffering.
Feel free to extend WrapDB.Meson is indeed nice, but has very poor support for GPU compilation compared to CMake. I've had a lot of success adopting the practices described in this talk, https://www.youtube.com/watch?v=K5Kg8TOTKjU. I thought I knew a lot of CMake, but file sets definitely make things a lot simpler.
Meson merges the crappy state of C/C++ tooling with something like Cargo in the worst way possible: by forcing you to handle the complexity of both. Nothing about Meson is simple, unless you're using it in Rust, in which case you're better off with Cargo.
In C++ you don't get lockfiles, you don't get automatic dependency install, you don't get local dependencies, there's no package registry, no version support, no dependency-wide feature flags (this is an incoherent mess in Meson), no notion of workspaces, etc.
Compared to Cargo, Meson isn't even in the same galaxy. And even compared to CMake, Meson is yet another incompatible incremental "improvement" that offers basically nothing other than cute syntax (which in an era when AI writes all of your build system anyway, doesn't even matter). I'd much rather just pick CMake and move on.
Build system generators (like Meson, autotools, CMake or any other one) can't solve programming language module and packaging problems, even in principle. So, it's not clear what your argument is here.
> I’m still surprised how people ignore Meson. Please test it :)
I did just that a few years ago and found it rather inconvenient and inflexible, so I went back to ignoring it. But YMMV I suppose.
> After reading about GNU Autotools
Consider Kitware's CMake.
Agreed, arcane cmake configs and or bash build scripts are genuinely off-putting. Also cpp "equivalents" of cargo which afaik are conan and vcpkg are not default and required much more configuring in comparison with cargo. Atleast this was my experience few years ago.
It's fundamentally different; Rust entirely rejects the notion of a stable ABI, and simply builds everything from source.
C and C++ are usually stuck in that antiquated thinking that you should build a module, package it into some libraries, install/export the library binaries and associated assets, then import those in other projects. That makes everything slow, inefficient, and widely dangerous.
There are of course good ways of building C++, but those are the exception rather than the standard.
"Stable ABI" is a joke in C++ because you can't keep ABI and change the implementation of a templated function, which blocks improvements to the standard library.
In C, ABI = API because the declaration of a function contains the name and arguments, which is all the info needed to use it. You can swap out the definition without affecting callers.
That's why Rust allows a stable C-style ABI; the definition of a function declared in C doesn't have to be in C!
But in a C++-style templated function, the caller needs access to the definition to do template substitution. If you change the definition, you need to recompile calling code i.e. ABI breakage.
If you don't recompile calling code and link with other libraries that are using the new definition, you'll violate the one-definition rule (ODR).
This is bad because duplicate template functions are pruned at link-time for size reasons. So it's a mystery as to what definition you'll get. Your code will break in mysterious ways.
This means the C++ committee can never change the implementation of a standardized templated class or function. The only time they did was a minor optimization to std::string in 2011 and it was such a catastrophe they never did it again.
That is why Rust will not support stable ABIs for any of its features relying on generic types. It is impossible to keep the ABI stable and optimize an implementation.
> C and C++ are usually stuck in that antiquated thinking that you should build a module, package it into some libraries, install/export the library binaries and associated assets, then import those in other projects. That makes everything slow, inefficient, and widely dangerous.
It seems to me the "convenient" options are the dangerous ones.
The traditional method is for third party code to have a stable API. Newer versions add functions or fix bugs but existing functions continue to work as before. API mistakes get deprecated and alternatives offered but newly-deprecated functions remain available for 10+ years. With the result that you can link all applications against any sufficiently recent version of the library, e.g. the latest stable release, which can then be installed via the system package manager and have a manageable maintenance burden because only one version needs to be maintained.
Language package managers have a tendency to facilitate breaking changes. You "don't have to worry" about removing functions without deprecating them because anyone can just pull in the older version of the code. Except the older version is no longer maintained.
Then you're using a version of the code from a few years ago because you didn't need any of the newer features and it hadn't had any problems, until it picks up a CVE. Suddenly you have vulnerable code running in production but fixing it isn't just a matter of "apt upgrade" because no one else is going to patch the version only you were using, and the current version has several breaking changes so you can't switch to it until you integrate them into your code.
It's not true that Rust rejects "the notion of a stable ABI". Rust rejects the C++ solution of freeze everything and hope because it's a disaster, it's less stable than some customers hoped and yet it's frozen in practice so it disappoints others. Rust says an ABI should be a promise by a developer, the way its existing C ABI is, that you can explicitly make or not make.
Rust is interested in having a properly thought out ABI that's nicer than the C ABI which it supports today. It'd be nice to have say, ABI for slices for example. But "freeze everything and hope" isn't that, it means every user of your language into the unforeseeable future has to pay for every mistake made by the language designers, and that's already a sizeable price for C++ to pay, "ABI: Now or never" spells some of that out and we don't want to join them.
I would suggest importing binaries and metadata is going to be faster than compiling all the source for that.
"That makes everything slow, inefficient, and widely dangerous."
There nothing faster and more efficient than building C programs. I also not sure what is dangerous in having libraries. C++ is quite different though.
Of course there is. Raw machine code is the gold standard, and everything else is an attempt to achieve _something_ at the cost of performance, C included, and that's even when considering whole-program optimization and ignoring the overhead introduced by libraries. Other languages with better semantics frequently outperform C (slightly) because the compiler is able to assume more things about the data and instructions being manipulated, generating tighter optimizations.
>There are of course good ways of building C++, but those are the exception rather than the standard.
What are the good ways?
"Do not do it" looks like the winning one nowadays.
In my experience, no one does build systems right; Cargo included.
The standard was initially meant to standardize existing practice. There is no good existing practice. Very large institutions depending heavily on C++ systematically fail to manage the build properly despite large amounts of third party licenses and dedicated build teams.
With AI, how you build and integrate together fragmented code bases is even more important, but someone has yet to design a real industry-wide solution.
Speedy convenience beats absolute correctness anyday. Humans are not immortal and have finite amount of time for life and work. If convenience didn't matter, we would all still be coding in assembly or toggling hardware switches.
C++ builds are extremely slow because they are not correct.
I'm doing a migration of a large codebase from local builds to remote execution and I constantly have bugs with mystery shared library dependencies implicitly pulled from the environment.
This is extremely tricky because if you run an executable without its shared library, you get "file not found" with no explanation. Even AI doesn't understand this error.
The Mars Polar Lander and Mars Climate Orbiter missions would beg to differ.
(And "absolute" or other adjectives don't qualify "correctness"... it simply is or isn't.)
I didn’t think header only was that bad - now we have a nightmare of incompatible standards and compilers.
No, because most major compilers don't support header units, much less standard library header units from C++26.
What'll spur adoption is cmake adopting Clang's two-step compilation model that increases performance.
At that point every project will migrate overnight for the huge build time impact since it'll avoid redundant preprocessing. Right now, the loss of parallelism ruins adoption too much.
No. Modules are a failed idea. Really really hard for me to see them becoming mainstream at this point.
The idea is great, the execution is terrible. In JS, modules were instantly popular because they were easy to use, added a lot of benefit, and support in browsers and the ecoysystem was fairly good after a couple of years. In C++, support is still bad, 6 years after they were introduced.
Exactly. C++ is still waiting for its "uv" moment, so until then modules aren't even close to solved.
And uv required some ground work, where the PEP process streamlined how you define a python project, and then uv could be built on top.
No idea if modules themselves are failed or no, but if c++ wants to keep fighting for developer mindshare, it must make something resembling modules work and figure out package management.
yes you have CPM, vcpkg and conan, but those are not really standard and there is friction involved in getting it work.
Much like contracts--yes, C++ needs something modules-like, but the actual design as standardized is not usable.
Once big companies like Google started pulling out of the committee, they lost their connection to reality and now they're standardizing things that either can't be implemented or no one wants as specced.
I emphatically agree. C++ needs a standard build system that doesn’t suck ass. Most people would agree it needs a package manager although I think that is actually debatable.
Neither of those things require modules as currently defined.
Can you explain why you think modules are a failed idea? Because not that many use them right now?
Personally I use them in new projects using XMake and it just works.
I'm not the PC but I think you miss most of the pain points due to: 'personal' projects.
There's not a compatible format between different compilers, or even different versions of the same compiler, or even the same versions of the same compiler with different flags.
This seems immediately to create too many permutations of builds for them to be distributable artifacts as we'd use them in other languages. More like a glorified object file cache. So what problem does it even solve?
Because as a percentage of global C++ builds they’re used in probably 0.0001% of builds with no line of sight to that improving.
They have effectively zero use outside of hobby projects. I don’t know that any open source C++ library I have ever interacted with even pretends that modules exist.
"Failed idea" gives modules too much credit. Outside old codebases, almost no one outside C++ diehards have the patience for the build and tooling circuss they create, and if you need fast iteration plus sane integration with existing deps, modules are like trading your shoes for roller skates in a gravel lot. Adopting them now feels like volunteering to do tax forms in assembbly.
I frankly wish we'd stop developing C++. It's so hard to keep track of all the new unnecessary toys they're adding to it. I thought I knew C++ until I read some recent C++ code. That's how bad it is.
Meanwhile C++ build system is an abomination. Header files should be unnecessary.
> Second, conforming compiler and standard library implementations are coming quickly. Throughout the development of C++26, at any given point both GCC and Clang had already implemented two-thirds of C++26 features. Today, GCC already has reflection and contracts merged in trunk, awaiting release.
How far is Clang on reflection and contracts?
Clang’s C++2c implementation status page simply says “no” for both reflection and contracts. GCC’s says “yes”.
https://clang.llvm.org/cxx_status.html
https://gcc.gnu.org/projects/cxx-status.html
std::execution is very interesting, but will be difficult to get started with, as cautioned by Sutter. This HPC Wire article demonstrates how to use standard C++ to benefit from asynchronously parallel computation on both CUDA and MPI:
https://www.hpcwire.com/2022/12/05/new-c-sender-library-enab...
Overlapping communication and computation has been a common technique for decades in high-performance computing to "hide latency", which leads to better scaling. Now standard C++ can be used to express parallel algorithms without tying to a specific scheduler.
I am actually excited for post and pre conditions. I think they are an underused feature in most languages.
Finally, reflection has arrived, five years after I last touched a line in c++. I wonder how long would it take the committee, if ever, to introduce destructing move.
Yeah I feel the same way. Lots of nice features that would have been helpful 5 years ago before I switched to Rust.
If you ask me (and why wouldn't you? :-)...) I really wish the C++ WG would do several things:
1. Standardize a `restrict` keyword and semantics for it (tricky for struct/class fields, but should be done).
2. Uniform Function Call Syntax! That is, make the syntax `obj.f(arg)` mean simply `f(obj, arg)` . That would make my life much easier, both as a user of classes and as their author. In my library authoring work particularly. And while we're at it, let us us a class' name as a namespace for static methods, so that Obj::f the static method is simply the method f in namespace Obj.
3. Get compiler makers to have an ABI break, so that we can do things like passing wrapped values in registers rather than going through memory. See: https://stackoverflow.com/q/58339165/1593077
4. Get rid of the current allocators in the standard library, which are type-specific (ridiculous) and return pointers rather than regions of memory. And speaking of memory regions (i.e. with address and size but no element type) - that should be standardized too.
Great. C++20 has been my favorite and I was wasn't sure what the standards says since it's been a while. I'll be reading the C++26 standard soon
I don't care until they stop pretending Unicode doesn't exist.
What are you talking about, there is actually too much unicode awareness in C++. Unicode is not the same thing as utf-8. And, frankly, no language does it right, I'm not even sure "right" exists with Unicode
Too much unicode in standard C++? Where?
c++20's u8strings took a giant steaming dump on a number of existing projects, to the point that compiler flags had to be introduced to disable the feature just so c++20 would work with existing codebases. Granted that's utf-8 (not the same thing as unicode, as mentioned) but it's there.
Sadly, transparent hash strings for unordered_map are out.
It is annoying that they didn't just apply this to all containers
As long as programmers still have to deal with header files, all of this is lipstick on a pig.
You don't on new projects. CMake + ninja has support for modules on gcc, clang, and MSVC.
This should be your default stack on any small-to-medium sized C++ project.
Bazel, the default pick for very large codebases, also has support for C++20 modules.
Thanks. It's been a long time since I started a C++ project, and I've never set up any build chain in Visual Studio or Xcode other than the default.
How about using Zig to build C++ projects?
I am curious what is their strategy to get language to the stage where the US government will make it cosher for new projects
No such strategy is necessary. That discourse was about not using C++ for applications where Java would work just as well.
The US government still uses C++ widely for new projects. For some types of applications it is actually the language of choice and will remain so for the foreseeable future.
>"For some types of applications it is actually the language of choice..."
Can you give an example please? And how does it correspond to government ONCD report and other government docs "recommending" "safe" languages like: Rust (noted for its ability to prevent memory-unsafe code), Go, Java, Swift, C#, Ruby, Ada
Among other things I design and implement high performance C++ backends. for some I got SOCS2 Type II certification but I am curious about future. Do not give a flying fuck about what the criteria for military projects as I would not ever touch one even if given a chance.
I look forwards to getting to make use of this in 2040!
Proper reflection is exciting.
GCC has it marked as 'RESOLVED FIXED' as of about a week and a half ago. So, it's coming.
Also, useful: https://gcc.gnu.org/projects/cxx-status.html
Support in GCC isn't what limits my usage of latest C++ at work.
"Japanese soldier who kept fighting 29 years after World War 2"
I watched a talk from Bjarne Stroustrup at CppCon about safety and it was pretty second hand embarrassing watching him try to pretend C++ has always been safe and safety mattered all along to them before Rust came along.
Well, there has been a long campaign against manual memory management - well before Rust was a thing. And along with that, a push for less use of raw pointers, less index loops etc. - all measures which, when adopted, reduce memory safety hazards significantly. Following the Core Guideliness also helps, as does using span's. Compiler warnings has improved, as has static analysis, also in a long process preceding Rust.
Of course, this is not completely guaranteed safety - but safety has certainly mattered.
Contracts feel like the right direction but the wrong execution timeline. The Ada/SPARK model shows how powerful contracts become when they feed into static verification — but that took decades of iteration on a language with far cleaner semantics. Bolting that onto C++ where UB is load-bearing infrastructure is a different beast entirely. The real risk isn't complexity for complexity's sake — it's that a "minimum viable" contracts spec gets locked in, and then the things that would actually make it useful for proof assistants become impossible to retrofit because they'd break the v1 semantics. Bjarne's concern about "incomplete" is more worrying to me than "bloated."
Nice try, clanker slop
Seeing that pic at the top of the article, and reflecting on my own experiences with rust: It is wild just how male-centric systems programming languages are. I'm from a career backround that's traditionally male-dominated (military aviation), but the balance is far more skewed among C, C++ and Rust developers.
Please don't use Hacker News as a religious or ideological battleground. It tramples curiosity. Please don't pick the most religiously/ideologically provocative thing in an article or post to complain about in the thread. Find something interesting to respond to instead.
This is interesting! People ignoring this, I think, is also interesting on its own. I respect if other people disagree, but that's my 2c. I think our overton windows may not agree here, but I think this is part of the value of discussions with other humans.
Are you a moderator? The directive tone of this post is as if from an authority figure, but, but I do not believe you are one.
I do not believe there is anything about a religious or ideological background here. Could you please clarify?
I also believe it is your post that could be more accurately described as trampling curiosity; I believe there is a role reversal, in that I think your comment is a better description for trampling curiosity than the post your are responding. I'm not trying to be snarky - I'm curious how you came to those conclusions.
The tone of the GP is such because it's a quote from the rules/guidelines. However, applying that rule to what you said makes no sense to me, fwiw.