Remix.run Logo
jcranmer 12 hours ago

> I wonder if C++ has some hairy concepts and syntax today on par with Rust's more difficult parts.

… … … … Unqualified name lookup has been challenging in C++ since even before C++11. Overload resolution rules are so painful that it took me weeks to review a patch simply because I had to back out of trying to make sense of the rules in the standard. There's several slightly different definitions of initialization. If you really want to get in the weeds, starting playing around with std::launder and std::byte and strict aliasing rules and lifetime rules, and you'll yearn for the simplicity of Rust.

C++ is the absolute most complex of any of the languages whose specifications I have read, and that's before we get into the categories of things that the standard just gives up on.

jandrewrogers 10 hours ago | parent | next [-]

> starting playing around with std::launder and std::byte and strict aliasing rules and lifetime rules, and you'll yearn for the simplicity of Rust

Annotations like std::launder, lifetime manipulation, etc solve a class of problems that exist in every systems language. They inform the compiler of properties that cannot be known by analyzing the code. Rust isn't special in this regard, it has the same issues.

Without these features, we either relied on unofficial compiler-specific behavior or used unnecessarily conservative code that was safe but slower.

tialaramex 7 hours ago | parent [-]

> Rust isn't special in this regard, it has the same issues.

This is both fundamentally true and misleading. Rust has to solve the same issues but isn't obliged to make all the same bad choices to do that and so the results are much better.

For example C++ dare not perform compile time transmutations so, it just forbids them and a whole bunch of extra stuff landed to work around that, but in Rust they're actually fine and so you can just:

    const FOO: bool = unsafe { core::mem::transmute::<i8, bool>(2) };
That blows up at compile time because we claimed the bit pattern for the integer 2 is a valid boolean and it isn't. If we choose instead 0 (or 1) this works and we get the expected false (or true) boolean instead of a compiler diagnostic.

C++ could allow this but it doesn't, rather than figure out all the tricky edge cases they just said no, use this other new thing we made.

jandrewrogers 6 hours ago | parent | next [-]

> For example C++ dare not perform compile time transmutations

I am confused by this assertion. You can abuse the hell out of transformations in a constexpr context. The gap between what is possible at compile-time and run-time became vanishingly small a while ago.

I think your example is not illustrative in any case. Many C++ code bases work exactly like your example, enforced at compile-time. That this can be an issue is a hangover from retaining compatibility with C-style code which conflates comparison operators and cast operators. It is a choice.

C++ can enforce many type constraints beyond this at compile-time that Rust cannot, with zero effort or explicit type creation. No one should be passing ints around.

germandiago 2 hours ago | parent | prev | next [-]

In my experience conversions is one of the things that maximum warning levels do excellent static analysis for nowadays. In the last 15 years I hardly had a couole problems (init vs paren initialization). All narrowing etc. is caught out of the box with warnings.

poppadom1982 3 hours ago | parent | prev [-]

I'm not sure what you're getting at but

const bool z = (const bool)((int8_t)2);

Is perfectly valid C++.

debugnik 3 hours ago | parent [-]

That's a conversion, not the same. The naive equivalent to transmute would be

    int8_t x = 2;
    bool y = *reinterpret_cast<bool *>(&x);
But reinterpret_cast isn't valid in a constexpr scope.
poppadom1982 2 hours ago | parent | next [-]

My point is, in your exact example both reinterpret_cast and C-style casts have the exact same behavior, making the example bad. If you want to showcase a deficiency of C++, it would make sense to pick something where the difference between cast types actually matters.

TuxSH an hour ago | parent | prev [-]

> But reinterpret_cast isn't valid in a constexpr scope.

std::bit_cast is

debugnik an hour ago | parent [-]

Oh cool, and it behaves like memcpy, not like pointer aliasing! I'm stuck with C++14 at work so I missed that one.

germandiago 2 hours ago | parent | prev | next [-]

The right strategy to use C++ efficiently is to set warnings to the maximum as errors and take the core guidelines or similar and avoid past cruft.

More often than not (except if you inherit codebases but clang has a modernize tool) most of the cruft is either avoidable or caught by analyzers. Not all.

But overall, I feel that C++ is still one of the most competitive languages if you use it as I said and with a sane build system and package manager.

pocksuppet 9 hours ago | parent | prev [-]

Unless you're writing a compiler, you should require the author of the patch to explain why it works.

jcranmer 9 hours ago | parent [-]

This was a patch for the compiler implementation of the changes to the standard.