D does compile time function evaluation every time there's a ConstExpression in the grammar. It did this back in 2007. It required no changes to the grammar or syntax, it just enhanced constant folding to be able to do function calls.
I don't understand why it is so complex in C++.
phire 12 hours ago [-]
We aren't talking about just compile time function evaluation of ConstExpressions. C++ had that all the way back in C++11, and many compilers were doing CTFE long before that as an optimisation (but as a programmer you couldn't really control when that would happen)
Compilers are always allowed to CTFE than what the C++ standard specifies, many do. The standard is just moving the lower bar for what all compilers must now evaluate at compile time, and what c++ programmer may expect from all compilers.
Since C++11, they have been massively extending the subset of the language that can be evaluated at compile time. C++14 allowed compiletime evaluation of loops and conditionals, along with local variables. C++20 allowed CTFE functions to do memory allocation (as long as it was also freed) along with calling object constructors/deconstructors.
The main new thing in C++26 is the ability to do CTFE placement new, which is something I remember missing the last time I tried to do fancy C++ stuff. Along with marking large chunks of the standard library as constexpr.
WalterBright 6 hours ago [-]
Function calls, loops, conditionals, local variables, memory allocation (via new), constructors/destructors, etc., were all there in D2007.
JonChesterfield 8 hours ago [-]
It has been a very exciting fifteen years. As a minor counter argument, other languages will let you run any code at compile time. Because it's code. And it can run whenever you want. C++ has that too I suppose, you just have to weave some of your control flow through cmake.
flohofwoe 4 hours ago [-]
Tbf, C and C++ did constant folding for function calls since forever too (as well as pretty much any compiled language AFAIK), just not as a language feature, but as an optimizer feature:
Because C++ is a completely overengineered boondoggle of a language where every little change destabilizes the Jenga tower that C++ had built on top of C even more ;)
JonChesterfield 8 hours ago [-]
Constexpr as a keyword which slowly applies in more places over decades is technically indefensible. It has generated an astonishing quantity of work for the committee over the years though which in some sense is a win.
I think there's something in the psychology of developers that appreciates the byzantine web of stuff which looks like it will work but doesn't.
kccqzy 12 hours ago [-]
I think being explicit is a good thing in C++. Suppose there is not constexpr in C++ and the following works:
inline int foo(int x) { return x + 42; }
int arr[foo(1)];
I think it would qualify as spooky action-at-a-distance if modifying foo causes arr to be malformed. And if they are in different libraries it restricts the ways the original function can be changed, making backwards compatibility slightly harder.
tialaramex 12 hours ago [-]
This would make more sense if constexpr was actually constant like say, Rust's const.
The Rust const fn foo which gives back x + 42 for any x, is genuinely assured to be executed at compile time when given a constant parameter. If we modify the definition of foo so that it's not constant the compiler rejects our code.
But C++ constexpr just says "Oh, this might be constant, or it might not, and, if it isn't don't worry about that, any constant uses will now magically fail to compile", exactly the spooky action at a distance you didn't want.
When originally conceived it served more or less the purpose you imagine, but of course people wanted to "generalize" it to cover cases which actually aren't constant and so we got to where we are today.
Calavar 10 hours ago [-]
Slapping the constexpr keyword on a function is useless by itself, but it becomes useful when you combine it with a constexpr or constinit variable. Which is not all that different from Rust:
// C++
constexpr Foo bar() { /* ... */ }
constexpr Foo CONSTANT = bar(); // Guaranteed to be evaluated at compile time
constinit Foo VARIABLE = bar(); // Guaranteed to be evaluated at compile time
// Rust
const fn bar() -> Foo { /* ... */ }
const CONSTANT: Foo = bar(); // Guaranteed to be evaluated at compile time
static VARIABLE: Foo = bar(); // May or may not be evaluated at compile time
So Rust is actually less powerful than C++ when it comes to non-constant globals because AFAIK it doesn't have any equivalent to constinit.
tialaramex 3 hours ago [-]
That Rust constant named CONSTANT, is an actual constant (like an old-school #define) whereas in C++ what you've named CONSTANT is an immutable variable with a constant initial value that the language promises you can refer to from other constant contexts. This is a subtle difference, but it's probably at the heart of how C++ programmers end up needing constfoo, constbar, const_yet_more_stuff because their nomenclature is so screwed up.
The VARIABLE in Rust is an immutable static variable, similar to the thing you named CONSTANT in C++ and likewise it is constant evaluted so it happens at compile time, there is no "may or may not" here because the value is getting baked into the executable.
If we want to promise only that our mutable variable is constant initialized, like the C++ VARIABLE, in Rust we write that as:
let mut actually_varies = const { bar() }; // Guaranteed to be evaluated at compile time
And finally, yes if we write only:
let mut just_normal = bar(); // This may or may not be evaluated at compile time
So, I count more possibilities in the Rust, and IMNSHO their naming is clearer.
whytevuhuni 7 hours ago [-]
static VARIABLE: Foo = bar(); // May or may not be evaluated at compile time
Under what circumstances would this not be evaluated at compile time? As far as I know the initializer must be const.
Unlike C++, Rust does not have pre-main (or pre-_start) runtime initializers, unless you use something like the ctor crate. And the contents of that static must be already initialized, otherwise reading it (especially via FFI) would produce unknown values.
To evaluate something at runtime you'd have to use a LazyCell (and LazyCell::new() is const), but the LazyCell's construction itself will still be evaluated at compile time.
pjmlp 9 hours ago [-]
True, but C++ being as it is, we now also have consteval and constinit.
WalterBright 6 hours ago [-]
That does work in D, has for 18 years now, and is one of D's best loved features. Nobody has asked for the constexpr keyword.
112233 5 hours ago [-]
Why? How is changing constness of foo different from changing return type of foo, or changing set of thrown exceptions, or argument set?
Especially now that it is a good practice to define c++ functions like
love seeing all the constant updates but sometimes i feel like the biggest pain points just stick around way too long - you ever feel like these standards chase the wrong problems sometimes?
kreetx 9 hours ago [-]
What are the right problems?
Davidbrcz 5 hours ago [-]
Sanity, language complexity,
nokeya 17 hours ago [-]
And in the wild (supported by all major compilers) we will see this somewhere around 2040…
nly 16 hours ago [-]
Not if you run RHEL:
dnf install gcc-toolset-X
pjmlp 16 hours ago [-]
GCC only started supporting C++20 modules in a usable form, last month, and there are still parts missing from C++20.
So expect at very least 2026 + 5 => 2031 for that command to provide a complete C++26 development experience.
ender341341 15 hours ago [-]
The compiler writers have had a ton of issues implementing modules and aren't particularly excited for them (the committee seems to have forgot the lessens learned from c++98 and not having full implementations for crazy hard things)
on the other hand constexpr changes tend to be picked up pretty quickly, and a lot of them tend to be things that the compiler/stl authors themselves are asking for.
pjmlp 9 hours ago [-]
Since C++14 that I have increasingly changed my mind that it should only be about existing practice, and no paper should be accepted without implementation, regardless of how basic it may be, just like in other language ecosystems.
Yes it might prevent lots of cool features, yet how good are they if it takes years to be implemented across compilers, or they turn out to be yet another export template.
Additionally it would help to reduce count from those 300+ people, not everyone is there for good reasons, some only want to have a "contributed to C++" on their background, and then they are gone.
WalterBright 13 hours ago [-]
C++ should have just copied D modules (and the modules that ImportC supports).
pjmlp 9 hours ago [-]
Maybe, however always telling what they should have done won't change the languages position on the market, so we get what we can have.
Many things D might have done it first, yet it is hardly acknowledged, or has any impact on adoption without a major backer.
I also keep telling WG14 should care about security, since Usenet days, fighting windmills.
WalterBright 7 hours ago [-]
Many features of D have since found their way into C++, such as ranges, compile time function execution, thousands separators in numeric literals, conditional compilation blocks, etc.
pjmlp 2 hours ago [-]
Indeed, and with them, the reasons for the industry to care about what D offers sadly diminishes.
Also some of those features predate D, having shown first in Ada, Common Lisp, Eiffel, as discussed in the past.
phire 11 hours ago [-]
Yeah, maybe they should have, would have been a lot simpler.
But C++ really wanted modules to be a more or less drop in replacement for #include (or at least a set of common use cases), which really pushed up the required level of complexity.
pjmlp 9 hours ago [-]
That it was already there via Apple and Google's work, header maps, but what we got was Microsoft proposal, after some collaboration with Google.
Note at WWDC 2024, the module improvements regarding build times, in what concerns C++, it is based on header maps as well. Apple is not even bothering with C++20 modules.
gpderetta 16 hours ago [-]
Sure but this is about constexpr, not modules.
pjmlp 9 hours ago [-]
Indeed, however there is a certain velocity how many years after the standard gets ratified until the compilers get fully compliant.
mkoubaa 14 hours ago [-]
Modules implicate the entire toolchain, backward compatibility, and binary compatibility. constexpr is a compiler feature. Wild that they are being compared.
pjmlp 9 hours ago [-]
See how many years it takes for a compiler on average to be 100% compliant after a standard is ratified, not widely at all when one wants to write portable code, regardless.
int_19h 4 hours ago [-]
In practice it's the same thing with C++ today as it is with web standards: you have to evaluate support on a feature-by-feature basis, and stick to the features that are supported on all the implementations you care about.
And constexpr in stdlib is much more likely to get quick adoption across all implementations than something like modules.
pjmlp 2 hours ago [-]
Not really, folks have done a good job turning the Web into ChromeOS, there is only one relevant browser still standing.
15 hours ago [-]
kubav027 15 hours ago [-]
One of my first tasks in my carrier was implementing stable sort to incomplete stl implementation. In 3 years no one used it. This so niche it should not be part of C++ standard.
brooke2k 17 hours ago [-]
seeing the words "constexpr sorting" makes all the compile-time sirens go off in my head
mkoubaa 14 hours ago [-]
We demand an opt out!
dvratil 16 hours ago [-]
Amazing, now could I just get a way to do asynchronous network requests in two lines of code, like I have with other languages?
Honestly, it seems to me like the committee is constantly chasing the easy bits but is failing to address the bigger issues in the language and, more importantly, the standard library.
int_19h 4 hours ago [-]
co_await etc is there, now it's a matter of libraries picking that up.
On Windows, you can do this today:
auto response {co_await httpClient.GetStringAsync(uri)};
I don't understand why it is so complex in C++.
Compilers are always allowed to CTFE than what the C++ standard specifies, many do. The standard is just moving the lower bar for what all compilers must now evaluate at compile time, and what c++ programmer may expect from all compilers.
Since C++11, they have been massively extending the subset of the language that can be evaluated at compile time. C++14 allowed compiletime evaluation of loops and conditionals, along with local variables. C++20 allowed CTFE functions to do memory allocation (as long as it was also freed) along with calling object constructors/deconstructors.
The main new thing in C++26 is the ability to do CTFE placement new, which is something I remember missing the last time I tried to do fancy C++ stuff. Along with marking large chunks of the standard library as constexpr.
https://www.godbolt.org/z/Kja5jrWo5
>I don't understand why it is so complex in C++
Because C++ is a completely overengineered boondoggle of a language where every little change destabilizes the Jenga tower that C++ had built on top of C even more ;)
I think there's something in the psychology of developers that appreciates the byzantine web of stuff which looks like it will work but doesn't.
The Rust const fn foo which gives back x + 42 for any x, is genuinely assured to be executed at compile time when given a constant parameter. If we modify the definition of foo so that it's not constant the compiler rejects our code.
But C++ constexpr just says "Oh, this might be constant, or it might not, and, if it isn't don't worry about that, any constant uses will now magically fail to compile", exactly the spooky action at a distance you didn't want.
When originally conceived it served more or less the purpose you imagine, but of course people wanted to "generalize" it to cover cases which actually aren't constant and so we got to where we are today.
The VARIABLE in Rust is an immutable static variable, similar to the thing you named CONSTANT in C++ and likewise it is constant evaluted so it happens at compile time, there is no "may or may not" here because the value is getting baked into the executable.
If we want to promise only that our mutable variable is constant initialized, like the C++ VARIABLE, in Rust we write that as:
And finally, yes if we write only: So, I count more possibilities in the Rust, and IMNSHO their naming is clearer.Unlike C++, Rust does not have pre-main (or pre-_start) runtime initializers, unless you use something like the ctor crate. And the contents of that static must be already initialized, otherwise reading it (especially via FFI) would produce unknown values.
To evaluate something at runtime you'd have to use a LazyCell (and LazyCell::new() is const), but the LazyCell's construction itself will still be evaluated at compile time.
Especially now that it is a good practice to define c++ functions like
dnf install gcc-toolset-X
So expect at very least 2026 + 5 => 2031 for that command to provide a complete C++26 development experience.
on the other hand constexpr changes tend to be picked up pretty quickly, and a lot of them tend to be things that the compiler/stl authors themselves are asking for.
Yes it might prevent lots of cool features, yet how good are they if it takes years to be implemented across compilers, or they turn out to be yet another export template.
Additionally it would help to reduce count from those 300+ people, not everyone is there for good reasons, some only want to have a "contributed to C++" on their background, and then they are gone.
Many things D might have done it first, yet it is hardly acknowledged, or has any impact on adoption without a major backer.
I also keep telling WG14 should care about security, since Usenet days, fighting windmills.
Also some of those features predate D, having shown first in Ada, Common Lisp, Eiffel, as discussed in the past.
But C++ really wanted modules to be a more or less drop in replacement for #include (or at least a set of common use cases), which really pushed up the required level of complexity.
Note at WWDC 2024, the module improvements regarding build times, in what concerns C++, it is based on header maps as well. Apple is not even bothering with C++20 modules.
And constexpr in stdlib is much more likely to get quick adoption across all implementations than something like modules.
Honestly, it seems to me like the committee is constantly chasing the easy bits but is failing to address the bigger issues in the language and, more importantly, the standard library.
On Windows, you can do this today: