Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

My experience with C++ is that every five years I come in and see people claiming that not only should I never do whatever I was told five years ago, but actually it was never popular and nobody ever did it. Mostly about ways to allocate objects or use smart pointers.

I'm still wondering why `(int)` is spelled `reinterpret_cast<int>` in C++. Do they just like hitting keys on the keyboard?



> I'm still wondering why `(int)` is spelled `reinterpret_cast<int>` in C++

1. because it is greppable and unsafe

2. because there is also static_cast and dynamic_cast

3. Because in C (int) is all three at once and not greppable -> C will let you do whatever, C++ will not let you do with a static_cast everything you can do with a (T) cast.

All in all, it is nice to ask, but if you do not know what you are talking about except for the surface, then, you should not say:

> Do they just like hitting keys on the keyboard?

No, we do not, but we hate even more to get an ungreppable, undecipherable (semantically) casting lost somewhere in several tens of thousands of lines of code :) This eases finding the suspicious code more easily.


A reinterpret_cast is something that shows up very rarely, so it’s fine that it’s not concise. And when it is used, it usually can do with a function naming it. I try to have a “no raw reinterpret_cast” view unless it’s chars to unsigned chars for string stuff. And as others have said, it’s grepable and can’t cast away constness. If I’m handing a const unsigned char* to a function taking a char* that I know wind modify the data, I don’t want that to be (char*)ptr. I want it to be const_cast<char*>(reinterpret_cast<const char*>(ptr)) because yikes, it should stand out because it’s awful.

And then I’d wrap that godawful cast in a function overloading the legacy C interface, so the overload has one job: to encapsulate the logic that the legacy function isn’t const correct. So then I’d have like void wrappedFoo(std::string_view s) { foo(const_cast<char*>(reinterpret_cast<const char*>(s.data())), s.size()); } with lots of comments about the cast.


> 1. because it is greppable and unsafe

But it is not unsafe, or rather, the C++ casts aren't more safe. They have the same semantics.

There are differences casting between class types, but with numeric types the issues are how to handle the value not fitting in (or being imprecise) in the destination type. Casting a value to int in C++ that overflows is still UB.

It doesn't fix C's other strange numeric issues either, like how `unsigned short` * `unsigned short` produces `int`.


C++ casts are safer. I think you are confusing concepts here.

There are C++-style casts that are compile-time errors that C would allow you to do in C style. For C everything is potentially a reinterpret cast basically.

C++ casts are safer than C's.


they are unsafe because if you try to cast something that's not valid the compiler won't let you. you can then choose to ignore it and be unsafe, or figure out the problem and fix it.


You can still cast INT_MAX to short no matter how you spell the cast, and the result won't be correct.


There are more casts than those. Anda reinterpet cast is a superset of a static cast.

If you want to narrow, you would use static cast, not reinterpret cast.

If you want to reinterpret a set of bytes as another object then you reinterpret cast (actuall use std::bit_cast, will catch more errors,). So yes, you can still do that but consciously.

In C you could even turn a cast that is essentially a static cast into a reinterpret cast by accident and the compiler would say nothing.


> If you want to narrow, you would use static cast, not reinterpret cast.

But if you do that, it's not safe! It's the same as C, which is not safe - it's "implementation defined".

Swift would trap on overflow here.


> But if you do that, it's not safe!

static_cast does narrowing foor the case mentioned. reinterpret_cast would just reinterpret the same bit pattern as another type.

Whether it is equivalent or not is another story. For example, casting a float to an int will narrow but also will convert the internal format.

You can catch some overflow by {} initialization.


You shouldn't be using INT_MAX for the last decade or so. That's why std::numeric_limits exist.


You want me to type `std::numeric_limits<int>::max()` in a forum comment? Bit long.


Seems to be found by grep fine:

  % echo 'int x = (int)1.2f;' > foo 
  % grep '(int)' foo
  int x = (int)1.2f;
Does greppable mean something else in this context?


I'm assuming they didn't mean "grep" in a literal sense, rather that it is easier to find keywords like "reinterpret_cast" when scanning your eyes through code, whereas with C-style casts you'd have a much harder time.


Yes I meant this. But grepping can also be a real problem.It is not just plain int what you cast.


Now find casts that are known to be as unsafe as reinterpret cast for the whole set of types your codebase deals with to audit a violation of the type system. What would you grep?

I would grep 'reinterpret_cast' and I would set warning as error for the c-style casts. So you can assume my codebase does not have any.

I will find any cast to reference, pointer, etc. for any type. How would you do tjat if you do:

(int)a ((int)(MyType*))

and many, many more...


That makes sense, thanks. Grepping for C syntax casts with arbitrary stuff between the parens is clearly not going to work.


It doesn't matter since (int) is in the C++ language regardless of how we feel about it, so it seems their point still stands.


> I'm still wondering why `(int)` is spelled `reinterpret_cast<int>` in C++. Do they just like hitting keys on the keyboard?

They are doing different things. For example, `reinterpret_cast<int>` cannot cast away constness but `(int)` can. There are other differences and you should never use C-style casts. In most C++ code bases I worked on, there were static code analysis checks pre merge that would prevent anybody from merging code with C-style casts to master for that reason.


This is a very good example of C++ being difficult to master. There are heaps of ways of doing one thing and usually there are just one or a few of those a good practice, but to explain the best practice's rationale, you need to know and tell a whole story. A simple cast even needs a story. Let alone smart pointers combined with normal pointers; ampersands and const qualifiers that have different meanings depending on where you put them and so on. You can fill a small book with explaining initializers, a big book with explaining templating. In the same amount of pages you can explain the complete ANSI C.


Absolutely. The reason for this is that C++ has been around for a long time while maintaining backwards compatibility (to C too mostly). I know no language where it's as important to have excellent automated tooling that restricts usage of the language for your project. Thankfully tooling is pretty good nowadays but there is very little documentation on how to start if you don't have an expert on your team to help with that.


I'm sure there's a quote about their logic - something to do with making it long and annoying to really make you think twice about it.


Most people in 2022 have plenty of free bytes on their disks so they don't really care about how long their code is. If you want short code, I can recommend Fortran 77, no identifiers longer than 6 characters allowed.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: