New C++ features in GCC 12
developers.redhat.com
developers.redhat.com
It's basically as if Google and Apple don't really give a damn about keeping up with C++ standards, so they always get pushed on the back burner.
Most of those people behind, care about the LLVM infrastructure and not so much about upstreaming clang stuff or improving ISO C++ compliance.
But yes, a lot of LLVM users don't care about C++ standard compliance at all, for example because they don't use C++.
That world is not today’s world. Projects that would have been written in C++ in the past are often written in a different language now. This leaves C++ being useful mostly for legacy codebases, of which adopting new standards quickly is not so important.
Apple on the other hand mostly uses C++ in very restricted scopes such as DeviceKit which are very limited and can't safely use the entire language anyway. Xcode-shipped Clang and libcxx are often pretty old, too. They don't really need C++ that much above what C++03 already had, so it's clearly an afterthought for them.
One guess is because they didn't get what they wanted within the committee.
If I remember correctly some discussions on /r/cpp.
As for Apple, OP is right, Metal Shaders are based on C++14 dialect, IOKit/DriverKit use an Embedded C++ dialect, and for everything else there is Objective-C/Swift, and they only need enough C++ to use LLVM to develop them.
- A post about a C++ ABI break being voted against: https://cor3ntin.github.io/posts/abi/
I would not be surprised if google (or others in favor of breaking the ABI for the c++ standard library) would refocus on their alternate non-std std-replacement-ish library implementations (like abseil) instead given the lack of willingness to fix things in std proper from the committee.
Frankly, Google should fork C++ and create a new language without the warts and a new standard library.
That's how we got golang.
Another example of this shift would be that Bitcoin’s reference impl (2009) is written in C++, whereas Ethereum’s reference impl (2015) is written in Go.
Go has more mindshare outside Google than on Google's critical infrastructure.
I meant a language by removing all the warts, not removing all the features. :)
Seriously, if Go offered manual/arena memory management and real generic programming - stuff like template template parameters, STL algorithms, auto-vectorization, etc - it could effectively replace C++.
https://www.youtube.com/watch?v=PFdKFoQxRqM
Now, one of the things which you may have noticed really annoys C++ proponents here on HN is when people say "What Rust does..." and well, maybe Vittorio should not have done that in his talk. But the reason he's saying it is that although Rust has very few new ideas it popularises lots of existing ideas from PL theory that were not seen in the kind of languages C++ developers had experience with before.
A few slides in, Vittorio explains that he's not offering language fragmentation or dialects, an ABI break, or yet more stuff every programmer needs to learn. Nevertheless, criticisms of Epochs generally say that this is language fragmentation, it introduces dialects, it's an ABI break, and every programmer will have to learn all this extra stuff so it's hopeless...
And of course there’s also WebKit and LLVM.
https://webkit.org/languages-staged-08192020
https://llvm.org/docs/CodingStandards.html#c-standard-versio...
Possibly an unpopular opinion, but personally I wish the C++ Standard Library was kept separate from the C++ language and have the C++ committee focus on the language features instead.
This is not an unpopular opinion.
I think reading it would convince you that the statement Google doesn't use modern C++ at all is false. It talks about modern features like CTAD.
I'm pretty sure people said that about C++ when Java became popular.
Rust seems like a fine language, Zig sounds interesting, and Go and Swift I'm not familiar with. But I don't really buy your argument.
I was part of one, whose goal was to replace a CORBA infrastructure based in HP-UX, with Websphere running on Solaris/Linux.
I'm not a Rust dev, but I am curious how things like epoll would work in Rust. In C (or C++) I place a pointer to a structure in the epoll `event.data.ptr` field. When an event is returned by epoll_wait() all the caller has to do is run a function on that instance in `event.data.ptr`.
It seems to me, that even with `unsafe`, you're not going to be able to do this in Rust unless all the code is unsafe. You cannot transfer ownership of the instance to `event.data.ptr`, can you mark the pointer or event structure itself as `unsafe`?
Why wouldn't std::mem::transmute (plus a little bit of hand-managed lifetime emulation) work here? It is unsafe, but doesn't require the unsafe modifier to be present on code beyond where the transmute happens. Unless by "all the code is unsafe" you mean related code is conceptually at risk of unsafety due to bugs caused by erroneous reinterpret-casts, then I think transmute should work here.
[1] https://github.com/tokio-rs/mio
[2] https://tokio-rs.github.io/mio/doc/mio/struct.Poll.html#impl...
It gets around putting a ptr into the event structure (that epoll will then return) by putting a tokenID into it instead (that epoll returns), that the connection_handler function will then use to retrieve the connection instance that it needs to work with.
IOW, in `C` I'd create an instance of a `struct connection_t` and `epoll_wait()` returns the pointer to the instance.
This way, it appears that an ID is placed into the event structure and the { ID : corresponding instance } tuple is stored in a hashmap. `epoll_wait()` returns the ID, the caller uses that ID to get an instance to work with.
Although, I could be wrong - I'm not a Rust dev.
Can you link to an explanation of, or an argument for, that claim?
The safety offered by Rust is not a thing to ignore. However modern C++ and the tooling has made enough progress in the area. It is extremely rare for me to allocate explicitly and in the couple cases where I do I always write deallocator first. It is ingrained in my nature along with some other related habits. Invalid memory access is also less likely with modern constructs. Along with memory sanitizers and practical experience with my own products I feel that Rust memory safety at this point does not offer much from my practical point of view.
I value very much time spent on compiling / building. From few experiments I made Rust is not any faster in this department. Rather the opposite.
As for C++ being a monster impossible to comprehend in its entirety. Agree and do not give a flying fuck. I am not there to explore language to its utmost depths. It is just a tool for me and nothing more. I do not get hung up on tools. As long as the tool lets me do my job with the reasonable ease (and subset of modern C++ definitely does) the rest does not matter. I do not feel inferior for not willing to spend the rest of my life trying to become Alexandrescu.
Still I love the utter madness that there's behind C++, and the fact you can basically do everything you set yourself to, no matter how crazy and weird it is, as long as you accept to put up with the madness. You can emulate a good 90% of what Rust does with traits if you really want to.
I suspect maybe because you love programming as a process. I write C++ (other languages as well) for living but I am my own company, so my primary concern is to deliver more while spending less. The language on it's own means zilch to me as long as it adequate.
"where I do I always write deallocator first" that sounds like good practise.
But software development is driven by marketers. Having the time to properly add in the features required for safety is important.
From a marketing perspective it is a waste of money. When a change needs to be made, some feature that once lived under the hood and needs to be "surfaced", using some pointer into a structure in an unsafe manner will take five minutes, properly assembling the structures, rearranging the code etcetera takes two days - those days come off the profit of the company (from the marketing perspective)
As a computer programmer it is fabulous to have the resources to do it safely. It is not the conditions that a lot of us (most of us?) work in.
User visible features. All that matters. Memory safety? What is that!
One example, https://iclg.com/practice-areas/cybersecurity-laws-and-regul...
For the last 20+ years I sit in the basement of my house where I design and build my products. Some for my company. Some for clients. Every once in a while I would get out and meet with perspective clients / subcontractors or for some meeting with existing ones. But it gets increasingly more rare and everything is done using Zoom/Skype/Remote Deployments/Couriers. The only reason I get out is mostly for fun - meeting friends, cycling, swimming etc.
Rust uses more generics in its idiomatic code than C++. If you're careful to split out your generic type impls to avoid excess code monomorphization, compile times can be manageable. Macro use brings similar concerns, but it's probably rarer unless you're relying on popular crates that need it such as serde.
I question whether those are actually competing in this same realm, for large organizations. Language adopotion is not just about the language, but also tooling, libraries, standard, etc which organizations have built up and rely on.
For example, at Apple C++ is used more internally than is exposed in public APIS. (I can't imagine the swift compilers performance scaling up to large code bases at this point.)
Can you speculate on other reasons, besides competition for the slower rate of adoption? Is it not as helpful as previous standards? Is it more difficult to implement?
It struggles on medium sized code bases.
And Xcode becomes flaky too, on medium code bases.
Works absolutely brilliantly on the small examples that are used to market it.
I have been using it for two years, and fundamental bugs in the system have not been fixed in that time. (The code inspection tools in the debugger are close to useless)
The last time I updated it my computer spent over three hours with the installer running at 100%. What on Earth was it doing? Mining bitcoin?
A whole lot of things are clearly going very wrong behind the scenes
The reason modern C++ is still the preferred language for some types of software is that it is significantly more expressive in critical ways, which has a substantial impact on software robustness, maintainability, and performance. You could use those other languages, but it would produce a substantially worse product and/or codebase.
To put it another way, there is often no way to translate an existing elegant modern C++ codebase into those other languages without major compromises to either the codebase or performance. And for the kinds of applications where C++ is preferred, those compromises are largely unacceptable. Maybe some of those other languages will approach the practical expressiveness of modern C++ for this kind of software eventually, but not anytime in the near future.
Rust and C++ are roughly equally expressive for high-performance, high-scale software. The reasons to use one or the other for new code have little to do with whether each language can do the job; clearly, both languages can. Rather, what dominates are factors like whether your build/deployment system is set up to handle one language or another, what your engineers are experienced in, whether you care about memory safety, etc.
Source: I'm one of the maintainers of Rust at a large organization you have heard of that very much cares about high-performance, high-scale systems software.
The learning curve of Rust should not be underestimated, IMHO. I do not know except by my own experience and "gossip", and would be interested in hearing from a bigger shop that has to deal with it.
Rust can make an experienced developer feel utterly clueless. The borrow checker is a very harsh mistress. Moving from C++ to Rust for a computer programmer with many years of experience in C++ must be a frustrating and humbling experience. The simplest things become so hard.
I love Rust, but I do not use it professionally (I would love to). I can clearly see that it is a step closer to what we have been waiting for, it may even be the destination. But I can imagine the pain of moving a programmer from 20 years of C++ into Rust. Becoming a novice again
Not sure how this is relevant in a conversation about C++, for which the notion of a learning curve is even hard to apply given that practically no one manages to write code that can be trusted to be free of basic defects in the language.
> Rust can make an experienced developer feel utterly clueless. The borrow checker is a very harsh mistress.
And rightly so. Rust is only unique in that you get the hangover before deploying the code in production. It's puzzling that some might see this as a drawback of the language.
Sky day one is very easy, yet it takes years to master.
Snowboard day one is going to be mostly about falling, unless you want into xgames, it is much easier to master afterwards.
It depends on which experience one wants to have.
Any conversation about C++ has to consider the costs of abandoning the ship
The borrow checker is not such a big deal. You should have learned the basics in undergraduate CS in the context of concurrency and/or databases. If you have done shared data parallelism in modern C++ (let's say with OpenMP), you are probably used to similar reasoning.
Borrow checking and lifetimes do make some design patterns difficult to use, which is a reason you should not use such patterns in Rust. But there are almost always ways of achieving the same goals with idiomatic Rust. It's a bit like having to unlearn gotos when transitioning from BASIC to more serious languages and their fancy "structured programming".
Stating that it takes 25 years of experience in order to become productive with Rust doesn't really sound like a positive note.
Keep in mind that your typical user of high-level programming languages already has a very hard time grasping the basic mechanics of C++, and Rust's memory management paradigm alone is renowned for having quite a steep learning curve when compared to C++.
This is especially true if you are actually intending to write production code. There's no way I'd have comfortable releasing my C++ code to production after only 2 months, but my Rust code was completely solid despite my lack of experience in the language.
My experience was the exact opposite.
To write correct and safe C++ code you need to be tracking things like lifetimes and ownerships anyway (even in modern C++). Rust allows you offload all of that cognitive overhead to the compiler, which greatly simplifies things.
There was a short period of time needed to get used to the borrow checker, but coming from C++ I understood why it was complaining about these things so the errors didn't come across as opaque or confusing.
Rust IS modern C++ in a stricter package IMHO. C++ still has some extra magic (constexpr, SFINAE) that in Rust is yet not doable or requires procedural macros, but the core language is definitely designed around concepts such as by-value and move semantics which are crucial concepts to use when writing modern C++ applications.
C++ is not memory safe, which is table stakes these days for any truly "high scale" software. Which is why most large-scale software projects have been written in Java, a memory-safe language, since the late 1990s (taking over from the previously common use of C++). And no, even the C++ Core Guidelines and "modern" coding style do not fully address this issue; they're way too clunky, whether or not legacy code is involved. Rust, even more so than Java in the mid-1990s, is uniquely positioned in offering better performance and safety than C++ and a lower complexity in development.
And NVidia rather uses Ada/SPARK instead of Rust for high integrity computing.
In regards to Java, while I do agree, there is still enough JNI being used there, hence why Project Panama, to improve such workflows where C++ is part of the story.
It's a good language to learn. I hesitate to consider it a replacement for asm/C/C++. Writing rust is hoping that the code you're porting to rust can adapt well to the restrictions, and if not, searching for esoteric and needlessly unsafe/verbose workarounds.
I don't see Go as a natural C++ competitor. Go primarily competes with Java and C#.
"For me, the reason I was enthusiastic about Go was just about the same time we were starting on Go, I read (or tried to read) the C++0x proposed standard. And that was the convincer for me." - Ken Thompson
17:45 mark here: https://www.youtube.com/watch?v=sln-gJaURzk
https://commandcenter.blogspot.com/2012/06/less-is-exponenti...
[1] https://gitlab.kitware.com/cmake/cmake/-/merge_requests/7210
[2] https://gitlab.kitware.com/cmake/cmake/-/merge_requests/7210...
https://nwcpp.org/April-2022.html
https://dlang.org/spec/importc.html#__import
The same technique could be used with C++.
1. add some syntax to import a module
2. create another instance of the compiler to compile that module and return the symbol table
3. have the importer, when it doesn't find a symbol in the symbol table, to search the symbol tables of the imports
I was interested in C++, not javascript.
I look at Java 18, C# 11/.NET 7, Python 3.10, and the knowledge about latest features, language and ecosystem changes across all major libraries for the last 25 years is hardly less complex.
Well, you should know the libraries that you're using in C++, but I don't think that's any different in Python or Java. Granted, the mental model is more complex in C++ but that's the price you pay for the amount of control you get.
Also, isn't undefined behavior a lot easier to come across in Python since it's dynamically typed?
Are you sure you master all language and library changes between versions, even minor ones?
What are the major differences between Python 1.5 and Python 3.10?
Very lucky ones, those that can do their work from scratch using latest version of everything, including the OSes where they get deployed.
Completely disagree.
This is very unlike language like most other languages where you can play around with little language specific knowledge and the worst thing that's likely to happen is a runtime exception.
I find c++ syntax terse and hard to read. A good example is the well know boost library. Even back in the days when I was coding in c++ on day to day basis, I have always found boost code hard to read and reason about.
Do you mean the code of the library itself, or code using that library?
Reading the code in that page, I felt as if I looked up only to find the night sky abuzz with otherworldly ships glowing in the dark. It made me anxious. And I wanted to run.
But now I'm wondering, is this my reaction because it's been more than 10 years since I touched any c++ or because the language got really complex? I'm curious what seasoned c++ developers think about this.
It's important that they're there, authors of very generic libraries may have to deal with them. But I'd much prefer to have seen something about modules in that list...
C++ is immensely complex. The amount you need to grok is very significant.
I wonder if that is somehow a necessary feature of a performant, GC-free, optionally low level language.
Could you, for example, create something with the simplicity of Go, (most of) the speed of C/C++, and no GC? What would it look like?
I’ve never written a line of Rust so no idea there.
The C++ language designers have made a terrific language with the ergonomics of a four-fingered glove. Nim fits your criteria but doesn’t have the popularity of C++.
If you're looking for a modern, GC-less go I think it'd be your best bet unlike, say Nim, which tries to be it's own language on the syntax side too much. Zig just feels like C, but with the semantics firmed up a bit.
I wish C++ had a few more cosmetic and convenient things that I've gotten used to in Swift. 1. Properties with get/set/didSet blocks. 2. Non-nullable types, ie `T` and `T?`. There's probably no way to add those, even as a GCC-specific non-standard extension.
In Swift (and Kotlin and others) the compiler can prove that you never get a null pointer error at runtime. If you want something that can be null, the type is `T?` (short for `Optional<T>`) instead of `T`. Then there are various syntactically convenient ways of unwrapping it and dealing with the null case. Eg `foo ?? bar` means give me `foo` if it's not null, otherwise `bar`. Or `foo?.x` means: get the property x of foo, if foo is not null.
I haven't used C++ much in recent years. If there's an equivalent, I'd love to know about it.
E.g. in
std::string str;
str is never null, and always a valid object, because it's a value, not a pointer like in most GC'ed or dynamic languagesstd::string str1{"Apple"}; std::string str2 = std::move(str1);
Isn't str1 in an invalid (err, unspecified?) state and you shouldn't use it? It's not `null` sure, but it's not good to use.
It might not be null, but I can't do much more with it than I can with null. I need to assign something new first
I have a hard time understanding why "I could just be using a new variable" follows from "it won't have a useful value".
In Swift you are not allowed to declare a variable without assigning some value or nil to it before the current scope ends.
The semantics of c++ object initialization are such that a constructor will always be called.
What's better is that sanitizers don't detect this form of undefined behavior! (Clean on memory,address,undefined.)
https://godbolt.org/z/7jsxnMs8E
also relevant: https://i.imgur.com/3wlxtI0.gifv
Also, uninitialized by itself does not mean invalid though ? For your example, I wonder if it would make sense to flag it: printf could be implemented in assembly or Fortran for what we know (a few popular libc implementations are done in c++ for instance), and as such I don't know how much sense it would make for its internal usage of the pointed value to be checked against the c++ rules. I'd assume the outcome would be different with std::format or std::cout for instance
I suppose a definition of valid that prohibited uninitialized members would preclude lots of useful stuff like container and buffer types.
From the msan documentation [1], the flaw with my earlier example is that `printf` isn't instrumented.
And to address your other question, I don't know if msan can instrument a function implemented in assembly. It definitely can't deal with something it didn't compile as the instrumentation is added during compilation.
It seems that on godbolt the platform library also isn't instrumented because the equivalent iostream code is msan clean [2]. I suppose that makes sense as it's allowing you to pass arbitrary options to the compiler.
In summary, msan can detect these uninitialized reads but it requires quite a lot of fiddling.
[1]: https://clang.llvm.org/docs/MemorySanitizer.html#handling-ex... [2]: https://godbolt.org/z/Gsxsfn9GT
shows nothing on godbolt, but when run on my local machine yields the expected
$ clang++ bar.cpp -fsanitize=memory -DFMT_HEADER_ONLY=1 -std=c++20
$ ./a.out
42
==269465==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x55ba20d00c8e in fmt::v8::appender fmt::v8::detail::write<char, fmt::v8::appender, int, 0>(fmt::v8::appender, int) (/tmp/a.out+0xb9c8e)
#1 0x55ba20cff0ac in fmt::v8::appender fmt::v8::detail::default_arg_formatter<char>::operator()<int>(int) (/tmp/a.out+0xb80ac)
#2 0x55ba20d6ef67 in char const* fmt::v8::detail::parse_replacement_field<char, void fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref)::format_handler&>(char const*, char const*, void fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref)::format_handler&) (/tmp/a.out+0x127f67)
#3 0x55ba20cfaca9 in void fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref) (/tmp/a.out+0xb3ca9)
#4 0x55ba20cf8a57 in fmt::v8::vprint(_IO_FILE*, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<fmt::v8::appender, char> >) (/tmp/a.out+0xb1a57)
#5 0x55ba20cf86c2 in fmt::v8::vprint(fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<fmt::v8::appender, char> >) (/tmp/a.out+0xb16c2)
#6 0x55ba20cf7cce in main (/tmp/a.out+0xb0cce)
#7 0x7f45c65b330f in __libc_start_call_main libc-start.c
#8 0x7f45c65b33c0 in __libc_start_main@GLIBC_2.2.5 (/usr/lib/libc.so.6+0x2d3c0)
#9 0x55ba20c6a444 in _start (/tmp/a.out+0x23444)
SUMMARY: MemorySanitizer: use-of-uninitialized-value (/tmp/a.out+0xb9c8e) in fmt::v8::appender fmt::v8::detail::write<char, fmt::v8::appender, int, 0>(fmt::v8::appender, int)
Exitingcompile-time evaluation was introduced in C++11 and expanded in subsequent versions of the language standard.
Doing
const int X = 33;
// ...
void something() {
int arr[X] = ...
}
works in both C and C++, but with a catch: in C++, X is evaluated at compile time (it has internal linkage), while in C this works only after C99 because it's actually a VLA.const int X = 33;
enum { Y = X };
$ clang -o cs cs.c -Wall -std=c11 -pedantic
cs.c:6:6: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant]
A = X,
$ gcc -o cs cs.c -Wall -std=c11
cs.c:6:9: error: enumerator value for ‘A’ is not an integer constant
6 | A = X,
| ^
In general, that's illegal ISO C and should always be rejected, but as you see that's not usually the case.The compiler constant folds fairly effectively anyway.
#define QEMU_BUILD_BUG_ON(x) \
typedef char qemu_build_bug_on[(x)?-1:1] __attribute__((unused));
So you can write stuff like: QEMU_BUILD_BUG_ON(sizeof (struct foo) == 128);
(for example if the struct is used for some network protocol and so it must be 128 bytes long). #define QEMU_BUILD_BUG_MSG(x, msg) _Static_assert(!(x), msg)
#define QEMU_BUILD_BUG_ON(x) QEMU_BUILD_BUG_MSG(x, "not expecting: " #x)It's not exactly the same thing, but it's probably what you were thinking of.
Starting with C11, it's possible to implement it without any non-standard extensions: https://stackoverflow.com/a/49480926
struct A {
int a;
int b;
A() : b(1), a(b) { }
};
. Here, the b field is used uninitialized because the order of member initializers in the member initializer list is irrelevant;
what matters is the order of declarations in the class definition.
(A related warning, -Wreorder, can be used to warn when the order of member initializers does not match the declaration order.)
I don't get it. Why b? Do they mean a? // implicitly, a = undefined;
// implicitly, b = undefined;
a = b;
b = 1;
So b is used when its value is undefined. The undefined value is assigned to a.It's not called "Temporarily unusual" behaviour or "Reliably crashing" behaviour or even "I bet it'll probably just skip that and do the next thing" behaviour, it's Undefined for a reason.
When they say “b is used uninitialized”, they’re referring to it’s usage in that snippet. It’s uninitialized when referenced as a value for “a”.
It’s pretty easy to read that text as suggesting that b would be uninitialized in subsequent code, but that’s (not true, as you suspect, and) not what it’s talking about.
Changing the fundamental grammar is a step too far. Compilers can warn or reject bare statements in control structures if you want.
The most likely reason in your case is the build system of the library you want to build uses some mix of POSIX shell script and GNU Make, which is ... hard to get right on Windows. It's not C/C++ specific, through installing a cross C compiler on Windows might involve more work than rustup add target (or not, if someone did the work and packaged the toolchain as .exe installer).
The only thing missing are some atomic library calls.
Optional and not expected to be supported by every compiler out there, otherwise they would be part of ISO requirements.
clang-cl[1] does.
LLVM is a library that happens to have Clang in front of it, whereas GCC still isn't really able to have multiple targets in the same binary.
Could probably be done but for actually assembling toolchains you generally only need one anyway so it's not that annoying most of the time.
Not if you intend to ship on multiple platforms. Having to run builds on the native platform or install a separate cross-compilation toolchain is incredibly annoying (and difficult to get right to the point that it's common to not even try). It would be a big usability win if C toolchains could get cross-compilation to Go or Zig levels of "just works".
But then again, most new C++ features seem bananas to me, so I wouldn't read into it.
In Rust, for instance, constant functions have to evaluate the same in both compile time and run time - you can't have a different code path depending on which. If you want that, make two functions.
Trivially:
constexpr bool is_even(int x) {
if (std::is_constant_evaluated()) {
return true;
} else {
return ((x % 2) == 0);
}
}
How would you unit test this in a way that catches that only the constant evaluation is broken? constexpr bool test_is_even() {
assert(is_even(0));
assert(!is_even(1));
// … more test coverage
return true;
}
int main() {
test_is_even(); // runtime coverage
static_assert(test_is_even()); // compile-time coverage
This lets us run all test cases at both runtime and compiletime. (The static_assert performs constant evaluation, and if an assert within test_is_even would fail, it won’t be a constant expression.)This feature is added to c++ not for fun but because if you can't have a run-time branch that will do some AVX-fu, or invoke some runtime function from BLAS or LAPACK, the whole "evaluate at compile-time" thing is... Not useless but not far either