8

First of all, this is not a duplicate of Why do we have reinterpret_cast in C++ when two chained static_cast can do it's job?.

I know situations where we cannot use even two chained static_cast to achieve that, what reinterpret_cast does. But is there any situation where I should prefer a two chained static_cast over a simple and more readable reinterpret_cast?

Community
  • 1
  • 1
0xbadf00d
  • 17,405
  • 15
  • 67
  • 107
  • 3
    reinterpret_cast for me is like a signal of bad design (or that i do smthing wrong). my advice : dont use reinterpret_cast – den bardadym Jul 06 '11 at 09:48
  • 2
    @den bardadym - I've written very much low level code for hardware devices in the past ... I hadn't could have this done, without using `reinterpret_cast`. I know what you mean and in most (except low level) cases it might be true, but not in general. – 0xbadf00d Jul 06 '11 at 12:03

6 Answers6

9

reinterpret_cast should be a huge flashing symbol that says THIS LOOKS CRAZY BUT I KNOW WHAT I'M DOING. Don't use it just out of laziness.

reinterpret_cast means "treat these bits as ..." Chained static casts are not the same because they may modify their targets according to the inheritence lattice.

struct A {
    int x;
};

struct B {
    int y;
};

struct C : A, B {
    int z;
};

C c;
A * a = &c;

int main () {
    assert (reinterpret_cast <B *> (a) != static_cast <B *> (static_cast <C *> (a)));
}

If you are not 100% sure that a points to a b, use dynamic_cast which will search for the above solution (albeit with a runtime cost). Bear in mind that this may return NULL or throw on failure.

I'm trying to think of times when I've actually used reinterpret_cast, there are really only two:

  • when a function is zipping/encrypting an arbitrary buffer and I want to use a const char * to traverse it
  • if(*reinterpret_cast<uint32_t*>(array_of_4_bytes_A) < *reinterpret_cast<uint32_t*>(array_of_4_bytes_B) or somesuch. Lines like this invite scrutiny and demand comments.

Otherwise if you have a A* which is really a B* then you probably want a union.

spraff
  • 32,570
  • 22
  • 121
  • 229
  • 2
    Note that the code with two static casts via `void*` is *just as crazy* as the `reinterpret_cast`, but you no longer have the huge flashing symbol. If you think it's crazy, you should leave the crazy-warning. If you think it isn't crazy, you should remove the crazy-warning. – Steve Jessop Jul 06 '11 at 10:26
  • 2
    Crazier. That big flashing crazy warning is important. Don't hide it. – David Hammen Jul 06 '11 at 10:28
  • @spraff - Of course, you shouldn't use reinterpret_cast with pointers to objects, unless you're absolutely sure that the current type `pointer-to-A` is actually of type `pointer-to-C`. However, it's still bad design. – 0xbadf00d Jul 07 '11 at 07:46
  • Before unrestricted unions in C++0x, there were a few legitimate cases for this (typically involving placement new also) but you had to be careful. http://www2.research.att.com/~bs/C++0xFAQ.html#unions – spraff Jul 07 '11 at 07:49
5

I for one would rather see reinterpret_cast <TargetType> (pointer_of_some_other_type) than static_cast <TargetType> (static_cast <void*> (pointer_of_some_other_type)) or static_cast <TargetType> ((void*) (pointer_of_some_other_type)) any time. That chain of casts going through void* is just a sneaky, underhanded way to avoid using the dreaded reinterpret_cast.

Many projects ban the use of reinterpret_cast unless a waiver is granted; the person who wrote the code needs to justify the use of the cast. IMO, a chain of static casts is worse (much worse!) than is reinterpret_cast. The chain has the same effects, the same problems as does a reinterpret_cast, but the chain does not have the benefit of being easy to find with a grep.

Addendum
Look at it this way. Case 1, you use reinterpret_cast, you go through all the project hoops to justify its use, the project manager grants a waiver. Months later, an error is traced to your use of dynamic_cast. You have a get out of jail free card. It is the project manager's ass that is on the line for giving you that card.

Case 2, you use the sneaky, underhanded chain of static casts and the code sneaks through peer review unscathed. Months later, an error is traced to your use of despicable technique. Your project manager might be in a bit of trouble for not catching this nastiness, but it is your ass that is on the line. You do not have that get out of jail free card. You do not pass Go. You go directly to the unemployment line.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • +1 Well said. I wonder who the headless anonymous downvoter was. – Cheers and hth. - Alf Jul 06 '11 at 10:39
  • 1
    "much worse" - although for the rare cases that the result of the two static casts is strict-alias-safe, and well-defined, there's a flaw in the C++03 standard that means the result of the reinterpret_cast is unspecified. Which is a shame, but as Alf says it's not something you worry about in practice. So you can decide (a) in our style, is reinterpret_cast a big flashy sign, and if so (b) is a big flashy sign appropriate for converting a pointer-to-POD into a `char*` at the first byte? If "yes, no": use the static casts or at the very least have a shortcut to get the waiver automatically. – Steve Jessop Jul 06 '11 at 10:50
  • For instance, one way to avoid jumping through hoops needlessly might be to use `boost:is_pod` to write your own cast-style template function that can do the conversion deemed safe. Whether that template uses reinterpret_cast or static_cast then is irrelevant, since it's one carefully-constructed use, and nobody else in the company has to write *either* cast, they can use the function. TBH, a `static_cast` from `void*` should be suspect anyway, if I'm going to sack the programmer for that then in good conscience I have to sack the PM and code reviewer too. – Steve Jessop Jul 06 '11 at 10:53
  • Your idea of a cast-style template is a nice workaround. That template can undergo deep scrutiny to ensure that it works right. In my world, programming standards typically are not hard and fast rules. Waivers can be granted (sometimes), sometimes easily, sometimes not, sometimes 15 months after the project ends. `mutable` is a forbidden keyword, for example. Getting a waiver for declaring a mutex to be mutable is a piece of cake. Volatile is also a forbidden keyword. Getting a waiver for using volatile is, well, a bit more volatile and dicey. – David Hammen Jul 06 '11 at 11:09
  • "sometimes 15 months after the project ends" - not as bad as I feared then. If you're allowed to do it (as the best you can manage right now) but leave it marked as a known issue for future confirmation or change, then that's a lot better than the code being held up in initial review until all stakeholders have been consulted. Once the code is in and available for testing, those stakeholders can decide for themselves whether they're able to make time to look at it before release :-) – Steve Jessop Jul 06 '11 at 14:57
  • @Steve Jesspo - How would one declare such a template. I don't think it's possible to suit every situation. – 0xbadf00d Jul 06 '11 at 14:58
  • @FrEEzE2046: well, the basic idea is `template char* get_chars(T *p) { return reinterpret_cast(p); }`. But first you have to handle cv-qualification of type T correctly, and then you have to limit what types `T` are allowed as the template parameter. This will be through some combination of specializations, type traits, SFINAE, compile-time assertions. As David suggests, it needs scrutiny to ensure that the template only instantiates for types that it's safe with. In fact you could allow any object type without seeing problems in practice on any implementation I've used. – Steve Jessop Jul 06 '11 at 15:02
  • Another useful tool, although I don't know at the moment if/how it could be implemented, would be something called `is_alias_safe`, which tells you whether the two types T and U can be aliased. That would let you extend `get_chars` to something that can *try* to cast to any type, although won't always work. It'd be easy to handle the builtin types just by listing the legal type-puns. Then the template would replace the type-pun warnings that GCC gives you. – Steve Jessop Jul 06 '11 at 15:06
2

Always use reinterpret cast as a last resort - it doesn't do any checking! - so if you can chain two, three or ten statements together that do some kind of validation on the operation then you've gained something worthwhile.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
2

Since, the listing of the scenarios can go very long, I am putting in simple words:

If chained static_cast<>s don't give compile-error, you should avoid reinterpret_cast<>.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • shouting doesn't make it more right. it's just a subtle forgot-to-include-that defect in the current standard, already fixed. andrei and sutter wrote guideline based on that omission, but since things are already fixed and since no compiler does things wrong following that guideline would be as silly as pretending a non-contiguous string buffer, or whatever based on purely formal problems in the past – Cheers and hth. - Alf Jul 06 '11 at 10:28
  • @Alf P. Steinbach, the shouting was not intended. :) – iammilind Jul 06 '11 at 10:30
  • 2
    I strongly disagree. Chained static casts that go through void* are far, far worse than reinterpret_cast. Give your project manager that huge flashing symbol. The manager and the code reviewers will see it and may have suggestions for how to avoid it. The chained static casts are hiding that big flashing sign when it should be there for all to see. – David Hammen Jul 06 '11 at 10:41
  • @David Hammen - I absolutely agree with you. Technically, `reinterpret_cast`s and chained `static_cast`s are the same. The only "benefit" you achieve is hiding what you're actually doing. – 0xbadf00d Jul 06 '11 at 12:07
  • Your emphasised line is correct **only** if you don't go through `void*` – spraff Jul 06 '11 at 15:27
1

You should not use reinterpret_cast in cases where cross-cast pointers - instead use implicit conversion to void*, then static_cast.

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Why is an implicit cast here a good choice? Afaik using implicit casts is as big a no go as using reinterpret_cast, but maybe you have good reasons for using it with void*? – KillianDS Jul 06 '11 at 10:18
  • 1
    @KillianDS: I changed the link - please see the linked question. – sharptooth Jul 06 '11 at 10:20
  • Uh, the discussion you link to is mostly mumbo jumbo. Looks formal but ain't. Don't propagate these urban myths, please_: they add tons of work with no advantage. – Cheers and hth. - Alf Jul 06 '11 at 10:44
0

Because in theory, they could do something different (although it's hard to imagine such a case). More importantly, they send different signals to the reader, and tell a different story to the compiler (which could affect optimization). Logically, I'd say use chained static_cast through void* for cases to/from a character type (e.g. dumping an image of the raw memory), and other cases which are well defined and portable, and reinterpret_cast when you're doing real low level, hardware dependent work: extracting the exponent field of a float by casting its address to an unsigned int*, and bit masking, for example.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 1
    Please a give an example of "they could do something different" (that's not just an example of the C++98 missing support for void* reinterpret_cast)? – Cheers and hth. - Alf Jul 06 '11 at 10:40
  • Personally, I would much rather have any conversion from void* to some other type be done through reinterpret_cast than static_cast. Project managers rightfully want to know when such goofiness is taking place. In fact, this may well become a rule on the projects over which I have some say on the coding standards. (That C++98 did not support this is so last millennium.) – David Hammen Jul 06 '11 at 10:52
  • @Alf what sort of example. The double `static_cast` has semantics defined by the standard, and is guaranteed to work (in the specific case mentioned). What `reinterpret_cast` does is implementation defined, and implementations have occasionally been known to be perverse. (Practically, I'll occasionally use `reinterpret_cast` in such cases, despite the inappropriate red flag it raises, because in practice, it works, even if in theory, it might not.) – James Kanze Jul 06 '11 at 11:18
  • @James Kanze - `5.2.10.[7] - reinterpret_cast` "A pointer to an object can be explicitly converted to a pointer to a different object type. When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1." – 0xbadf00d Jul 06 '11 at 12:59
  • @FrEEzE2046 In my copy, 5.2.10/7 reads "A pointer to an object can be explicitly converted to a pointer to an object of different type. Except that converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified." You must be looking a future version of the standard. – James Kanze Jul 06 '11 at 13:10
  • 1
    @James Kanze - I'm sorry, you're right. I've quoted from the newest C++0x draft. – 0xbadf00d Jul 06 '11 at 13:45
  • "_The double static_cast has semantics defined by the standard, and is guaranteed to work (in the specific case mentioned)._" Where is that guarantee written? – curiousguy Dec 20 '11 at 23:33