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.
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.
C++26 adds destructive moves. They are called relocatable types.
There are edge cases where destructive moves are not safe and it is impossible for the compiler to know they aren't safe. C++ uses non-destructive moves when it can't prove the safety of destructive moves, even if destructive moves may in fact be safe. C++26 adds a type annotation that guarantees destructive moves are safe in cases where you can't prove they are un-safe.
The concept of relocatable types is actually a bit broader in scope than just destructive moves, but destructive moves are one of the things it enables. It is a welcome change.
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.
I wonder if C++ already has so much complexity, that it would actually be a good idea to ignore feature creep, and implement any feature with even the most remote use-case.
It sounds (and probably is) insane. But if a feature breaks backwards compatibility, or can't be implemented in a way that non-negligibly affects compiler/IDE performance for codebases that ignore it, what's the issue? Specifically, what significant new issues would it cause that C++’s existing bloat hasn’t?
Contracts are already informally a thing: most functions have preconditions, and if you break those preconditions, the function doesn't make any guarantees of what it does.
We already have some primitive ways to define preconditions, notably the assert macro and the 'restrict' qualifier.
I don't mind a more structured way to define preconditions which can automatically serve as both documentation and debug invariant checks. Though you could argue that a simpler approach would be to "standardize" a convention to use assert() more liberally in the beginning of functions as precondition checks; that a sequence of 'assert's before non-'assert' code should semantically be treated as the functions preconditions by documentation generators etc.
I haven't looked too deep into the design of the actual final contracts feature, maybe it's bad for reasons which have nothing to do with the fundamental idea.
> 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.
I don't think this opinion is well informed. Contracts are a killer feature that allows implementing static code analysis that covers error handling and verifiable correct state. This comes for free in components you consume in your code.
> 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.
Offtopic, but this is a problem in the web world, too. Once something is on a standards track, there are almost mechanisms to vote "no, this is bad, we don't need this". The only way is to "champion" a proposal and add fixes to it until people are somewhat reasonably happy and a consensus is reached. (see https://x.com/Rich_Harris/status/1841605646128460111)
> 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.
This is a common sentiment about C++, but I find it very interesting that everyone seems to have a different feature in mind when they say it.
C++ isn't the first language to do things, but was/is often the first mainstream language to do things.
And then people complain about C++ for doing it wrong, or its complexity, and show language 'X' that does it better/right, but only because they saw C++ do it first, and 'not quite right'.
I expect contracts to be similar - other languages will watch, learn, and do version two, and then complain about c++, etc.
It took 'quite a while' to get rid of auto_ptr, for example.
If it wasn't for the fact this is a language feature, it would be better off in boost where it can be tested in the wild.
Is there any good documentation about contracts? https://en.cppreference.com/w/cpp/language/contracts.html is incredibly confusing - its first displayed example seems to be an edge case where the assertion itself causes a mutation?
> 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.
I don't understand this at all. There are modules.
But headers are perfectly fine to deal with and have been for decades and decades! Next you'll be arguing that contents pages in all books should be removed.
What an absurd attitude. The gymnastics to avoid circular inclusions alone are something nobody should be giving a thought to in the 2000s.
"But headers are perfectly fine to deal with and have been for decades and decades!"
I would have thought this was a joke... but your follow-up indicates otherwise.
So what are you going to float next? "So what if people broke their arms hand-cranking their cars to start them! People did it for years, so we shouldn't move to electric starters! Or automatic chokes!"
"And these electric ice boxes are for the birds! Speaking of which: People flying through the air in machines? POPPYCOCK!"
I envy the person that will walk into a c++ codebase and see "[[indeterminate]]" on some place. And then they will need to absolutely waste their time searching and reading what "[[indeterminate]]" means. Or over time they will just learn to ignore this crap and mentally filter it out when looking at code.
Just like when I was learning rust and trying to read some http code but it was impossible because each function had 5 generics and 2 traits.
On a quick read of the paper, I see two surprising things:
1. If there’s no initializer and various conditions are met, then “the bytes have erroneous values, where each value is determined by the implementation independently of the state of the program.
What does “independently” mean? Are we talking about all zeros? Is the implementation not permitted to use whatever arbitrary value was in memory? Why not?
2. What’s up with [[indeterminate]]? I would expect “indeterminate” to mean that the variable has a value that happens to be arbitrary (and may contain sensitive data, etc), not that it turns back into actual UB.
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."
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.
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.
2 sounds good, but it will break a lot of existing code what suddenly does something different. At least so far every version of the rules someone has come up with has had a real world example of code that would be seriously broken if it was in place.
re 3, clang has [[trivial_abi]] (and I believe GCC is also implementing it. But it won't be applied to standard types by default, because of course is ABI breaking. You'll have to derive your own.
I switched from C++ to Java/Python 20 years ago. I never really fit in, I just dont understand when people talk about the complicated frameworks to avoid multithreading/mutexes etc when basic C++ multi threading is much simpler than rxjava or async/await or whatever is flavor of the month.
But C++ projects are usually really boring. I want to go back but glad I left. Has anyone found a place where C++ style programming is in fashion but isn't quite C++? I hope that makes sense.
That place is C++/Lua, with most of the code in Lua. Concurrency model is thread-per-lua-state. Interthread comms is message passing implemented via textbook c++11 atomic and condition variables. Lua is just a small C library so the style remains very friendly to those who like C with classes.
It looks like they didn't even add _BitInt types yet. Adding concepts but not adding _BitInt types sounds insane considering how simple _BitInt types are as a programmer (not sure about implementation but it already works in clang).
Hosting the meeting in Croydon and not letting people leave until the thing is signed-off is definitely a cunning strategy. Never want to work down there again, ever.
Quite unrelated to the main topic, but shouldn't it be Croydon, London? I have never heard anyone called it London Croydon before. Generally addresses/places go from most specific to least and given Croydon is an area of London it should go first.
C++ is so tantalizingly close to being an amazing embedded c++ language if they could JUST support first-class polymorphism.
Embedded is such a perfect fit for interface-based programming, but because it cant determine call resolution outside of a single source file, EVERYTHING gets vtable'd, which ruins downstream optimizations.
There's some ugly workarounds.... CRTP, c-style (common header + different source files. To the person who says "use templates!".... no. I dont like templates. They are verbose, complex, and every time i try to use them they I end up foot-gunning myself. Maybe its a skill issue, but if you designed something that most people cant figure out, I'd argue the design is wrong.
C++ is SOOO close to doing compile-time polymorphism. If just needs a way to determine type across source files, which LTO sorta-kinda-but-not-really does.
I've seen some examples of C++ contracts replacing CRTP, but it used templates, which again, not a fan of.
53 comments
[ 3.3 ms ] story [ 72.6 ms ] threadProper reflection is exciting.
eg. B = std::move(A); // You are worried about touching A when it's in this indeterminate state?
There are edge cases where destructive moves are not safe and it is impossible for the compiler to know they aren't safe. C++ uses non-destructive moves when it can't prove the safety of destructive moves, even if destructive moves may in fact be safe. C++26 adds a type annotation that guarantees destructive moves are safe in cases where you can't prove they are un-safe.
The concept of relocatable types is actually a bit broader in scope than just destructive moves, but destructive moves are one of the things it enables. It is a welcome change.
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.
> bloated committee design and also incomplete
That's truly in that backdoor alley catching fire
It sounds (and probably is) insane. But if a feature breaks backwards compatibility, or can't be implemented in a way that non-negligibly affects compiler/IDE performance for codebases that ignore it, what's the issue? Specifically, what significant new issues would it cause that C++’s existing bloat hasn’t?
C++20 isn't fully implemented in any one compiler (https://en.cppreference.com/w/cpp/compiler_support.html#C.2B...).
We already have some primitive ways to define preconditions, notably the assert macro and the 'restrict' qualifier.
I don't mind a more structured way to define preconditions which can automatically serve as both documentation and debug invariant checks. Though you could argue that a simpler approach would be to "standardize" a convention to use assert() more liberally in the beginning of functions as precondition checks; that a sequence of 'assert's before non-'assert' code should semantically be treated as the functions preconditions by documentation generators etc.
I haven't looked too deep into the design of the actual final contracts feature, maybe it's bad for reasons which have nothing to do with the fundamental idea.
I don't think this opinion is well informed. Contracts are a killer feature that allows implementing static code analysis that covers error handling and verifiable correct state. This comes for free in components you consume in your code.
https://herbsutter.com/2018/07/02/trip-report-summer-iso-c-s...
Asserting that no one wants their code to correctly handle errors is a bold claim.
Offtopic, but this is a problem in the web world, too. Once something is on a standards track, there are almost mechanisms to vote "no, this is bad, we don't need this". The only way is to "champion" a proposal and add fixes to it until people are somewhat reasonably happy and a consensus is reached. (see https://x.com/Rich_Harris/status/1841605646128460111)
This is a common sentiment about C++, but I find it very interesting that everyone seems to have a different feature in mind when they say it.
And then people complain about C++ for doing it wrong, or its complexity, and show language 'X' that does it better/right, but only because they saw C++ do it first, and 'not quite right'.
I expect contracts to be similar - other languages will watch, learn, and do version two, and then complain about c++, etc.
It took 'quite a while' to get rid of auto_ptr, for example.
If it wasn't for the fact this is a language feature, it would be better off in boost where it can be tested in the wild.
https://en.cppreference.com/w/cpp/language/function.html#Fun... is vaguely better, but still quite dense.
IMO the syntax makes things hard for a newcomer to the syntax to understand, which I see as core to any programming language's goals of community.
would have been far more self-evident than just But I suppose brevity won out.How far is Clang on reflection and contracts?
It has been on compiler explorer for a while
This has been used to add reflection to simdjson
But headers are perfectly fine to deal with and have been for decades and decades! Next you'll be arguing that contents pages in all books should be removed.
"But headers are perfectly fine to deal with and have been for decades and decades!"
I would have thought this was a joke... but your follow-up indicates otherwise.
So what are you going to float next? "So what if people broke their arms hand-cranking their cars to start them! People did it for years, so we shouldn't move to electric starters! Or automatic chokes!"
"And these electric ice boxes are for the birds! Speaking of which: People flying through the air in machines? POPPYCOCK!"
It does have a runtime cost. There's an attribute to force undefined behavior on read again and avoid the cost:
Just like when I was learning rust and trying to read some http code but it was impossible because each function had 5 generics and 2 traits.
1. If there’s no initializer and various conditions are met, then “the bytes have erroneous values, where each value is determined by the implementation independently of the state of the program.
What does “independently” mean? Are we talking about all zeros? Is the implementation not permitted to use whatever arbitrary value was in memory? Why not?
2. What’s up with [[indeterminate]]? I would expect “indeterminate” to mean that the variable has a value that happens to be arbitrary (and may contain sensitive data, etc), not that it turns back into actual UB.
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.
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.
Someone has to bring a written spec to WG21 meetings and push it through.
And like in every open source project that doesn't go the way we like, the work is only done by those that show up.
But C++ projects are usually really boring. I want to go back but glad I left. Has anyone found a place where C++ style programming is in fashion but isn't quite C++? I hope that makes sense.
Embedded is such a perfect fit for interface-based programming, but because it cant determine call resolution outside of a single source file, EVERYTHING gets vtable'd, which ruins downstream optimizations.
There's some ugly workarounds.... CRTP, c-style (common header + different source files. To the person who says "use templates!".... no. I dont like templates. They are verbose, complex, and every time i try to use them they I end up foot-gunning myself. Maybe its a skill issue, but if you designed something that most people cant figure out, I'd argue the design is wrong.
C++ is SOOO close to doing compile-time polymorphism. If just needs a way to determine type across source files, which LTO sorta-kinda-but-not-really does.
I've seen some examples of C++ contracts replacing CRTP, but it used templates, which again, not a fan of.