Hacker News new | ask | show | jobs
by uecker 31 days ago
The "stronger type system" is mostly a myth in my opinion. It was true in the past in pre-prototype C. The void pointer rules are better in C IMHO as they avoid unneeded casts (that then remove more type safety) and FAMs and variably-modified types can express things C++ simply can't do well.
3 comments

I don't get your point at all. C++ has different casting operators (static_cast, const_cast, reinterpret_cast) that are strictly safer than C-style casts.

Also, let's not forget that implicit casts between unrelated pointer types is only a warning in C. Fortunately, modern C compilers started treating it as an error by default because it caused so much harm: https://gcc.gnu.org/gcc-14/porting_to.html. In C++ this was always a compiler error.

> implicit casts between unrelated pointer types is only a warning in C

A warning in C has the meaning of a "stern warning" aka. "That very much won't work, I warned you!". An error means, "I literally, don't what you mean".

Also as far as I know, the C standard only talks about diagnostics.

You are right that the C standard only talks about diagnostics, but this doesn't change the fact that implicit casts between unrelated pointer types are not treated as hard errors.
That's what I addressed in the first paragraph, a warning in C is a serious thing, not just some random nuisance.
Ok, but then compilers should really throw errors for these diagnostics by default. Why lump serious issues like incompatible pointer casts together with benign things like uninitialized variables?

(Another pet peeve of mine is that missing returns in functions is not considered a hard error. GCC 16 does not even issue a warning by default when compiling C code, which is just crazy. "-Werror=return-type" is the first thing I do in every new C or C++ project. I don't understand how this is still not the default...)

> Ok, but then compilers should really throw errors for these diagnostics by default.

That's because the default mode for compilers is to just shut up and compile this throw away code. Programmers know that you are expected to turn the warnings on and also nobody is manually invoking compilers directly. The default for build systems is often "-Wall -O2" anyway. Personally I prefer the status quo, I hate tools, that I just invoke and they start spamming my terminal. "Like, I know, that there will be issues, that's why I'm using you, but you don't need to tell me until I told you what exactly you are supposed to report me. Don't spam me until I ask you."

> Why lump serious issues like incompatible pointer casts together with benign things like uninitialized variables?

?? Uninitialized variables are also serious issues and also undefined behaviour. If anything they're more serious, because for pointer casts, it is likely that the types are compatible, but the compiler just doesn't know it, while truly uninitialized variables are always a real error. For the latter, the issue is rather, that the compiler often can't proof whether the variables are actually uninitialized. That's why it is more likely to meet a "maybe-uninitialized", a true "uninitialized" is rare in the wild.

I don’t understand your point at all, C++ objectively has a much stronger type system. It’s turing complete!

I’m not arguing that that’s better, or worse, but it’s definitely true and by no means a myth.

I don't think GP meant "it's completely made up", I think he meant the distinction doesn't matter most of the time.

I.e. most of the time the typing in real C++ code isn't meaningfully stronger than that found in C code.

More complex != stronger. A weak type system would imply that the type systems forgives type mismatches. C had this before it imported prototypes from C++ where you could call a function without declaring it first and if you got it wrong you got some error or crash. The only part where I think C++'s type system is meaningfully stronger than C are enumeration types.
> The void pointer rules are better in C IMHO as they avoid unneeded casts

...so much this! A void pointer is an "any-pointer" by design. It shouldn't require casting from and to specific pointer types, that defeats the whole point of having void pointers in the first place.

> It shouldn't require casting from and to specific pointer types

You don't need to explicitly cast T* to void* (guaranteed to be safe), you only need to cast when converting out of void*.

The rules are basically the same as casting between pointer-to-derived-class and pointer-to-base-class and they make sense.

They make sense but reduce type safety, because once you add the cast the case might hide some real typing issue. I sympathize with the idea that the down-cast should be explicit though.
> They make sense but reduce type safety

Yes, downcasting can be unsafe and should be used carefully, but what's the alternative? At least in C++ you can't cast between unrelated types without an explicit reinterpret_cast (or C-style cast).

and you CAN use static_cast to convert from void*; this silently keeps working if you refactor the void* into a matching-type pointer later, while raising a compilation error if you refactor to a different-type pointer.
Yes, a static_cast would be safe, but then most C++ seems to use a C-style cast because it is less clunky and then is less safe than the corresponding C code. The issue is not that the downcast is unsafe (it is, but once you have a void pointer you already accepted this), but that it becomes even less safe by adding a C-style cast.

It is also not clear what is gained by forcing programmers to add a cast. Void pointers should be used sparingly anyway.

The point is that, there is nothing else to be done with a void pointer, other then downcasting it, and it needs to be downcasted to be used. There is no other check that somehow validates the cast, so there is no upside to requiring the cast, while it does potentially silence accidental casts once the type changed.