I have read in multipul places that () is bad to do and that reinterpret_cast is the right way of converting varibles. Why? The compiler has no problem with me using (). So what makes it so wrong?
This is a discussion on What's the fuss about? within the C++ Programming forums, part of the General Programming Boards category; I have read in multipul places that () is bad to do and that reinterpret_cast is the right way of ...
I have read in multipul places that () is bad to do and that reinterpret_cast is the right way of converting varibles. Why? The compiler has no problem with me using (). So what makes it so wrong?
Terminology: () is a C-style cast, *_cast is the C++-style cast.
1) Lack of differentiation. What if the C-style your cast will do whatever is necessary to achieve the target. If you meant to truncate a double to an int, it will do that. If you meant to cast a pointer to an integral type, it will do that too. If you mean to expand a short into an int explicitly to resolve ambiguity, it will do that. And if you mean to remove a const qualifier from a pointer because of a legacy function that isn't const-correct, it will do that.
The C++-style has different keywords for different tasks. If you do a simple, rather safe cast, such as promoting short to int or truncating a double to int, you can use static_cast. Try anything more complex with it, and the compiler will complain. This ensures you don't accidently cast to some weird type when all you meant to do is truncate. E.g. look at this code:
This will compile, but it will probably emit a warning. Now you apply a C-style cast to tell the compiler, yes, you know it's truncating, but as fate will have it, you accidently remove the asterisk:Code:void foo(double *val) { int v = *val; }
Now this code will compile (perhaps with a warning on 64-bit systems, perhaps not). It will also not do what you want: it will give v the value of the pointer passed in, not the pointed-to value, truncated.Code:void foo(double *val) { int v = (int)val; }
C++-style, you would have done this:
This won't compile: static_cast cannot cast from pointer types to integral types. You just saved yourself from a very hard-to-find bug.Code:void foo(double *val) { int v = static_cast<int>(val); }
The second cast operator, const_cast, applies to our last example. It will cast the const and volatile modifiers from and to types. (Of course, the addition of them is implicit, so they're rarely used this way.)
_TCHAR is a special type defined in tchar.h: it is a typedef for char normally, but if the macro _UNICODE is defined, it becomes a typedef for wchar_t. This is very useful in developing for Win9x, 2k and CE at the same time.Code:#include <tchar.h> // Function guarantees not to modify the string, but alas, nobody made the parameter const. void legacy(char *str); void foo(const _TCHAR *txt) { legacy(txt); // Doesn't compile. }
OK, once again, you solve this with a C-style cast:
Now the code compiles. And now it's freaking dangerous: you forgot that _TCHAR might be wchar_t at one point, so you fail to provide for that case, where you should actually call wlegacy(), not legacy(). What happens when someone compiles as Unicode?Code:#include <tchar.h> // Function guarantees not to modify the string, but alas, nobody made the parameter const. void legacy(char *str); void foo(const _TCHAR *txt) { legacy((char*)txt); }
Answer: it still compiles. And legacy() will be very confused by getting a rather meaningless sequence of bytes: typically (if the first character of the string has a code point within 0-255, as would be the case for English text) it will see just the first character of a string, followed by a null byte.Code:#define _UNICODE #include <tchar.h> // Function guarantees not to modify the string, but alas, nobody made the parameter const. void legacy(char *str); void foo(const _TCHAR *txt) { legacy((char*)txt); }
Had you used a const_cast, it would now fail and you would be alerted to the fact that you have to account for these circumstances:
reinterpret_cast, finally, is the thing that will do just about anything (except the safe casts the other operators are for). And having to use reinterpret_cast is a good sign that somewhere there might be a design issue.Code:#include <tchar.h> // Function guarantees not to modify the string, but alas, nobody made the parameter const. void legacy(char *str); void foo(const _TCHAR *txt) { legacy(const_cast<char*>txt); // Compiles only if _TCHAR is char. }
2) Templates. When you don't know the actual types you get, limiting the stuff a cast may do is more important than ever.
3) Searchability. Imagine your program crashes with an access violation: a pointer is set to nirvana. Now there's typically four causes for this kind of bug.
a. Uninitialized variables.
b. Referencing pointers after they've been deleted.
c. Overwriting the pointer due to a buffer overflow.
d. Bogus casts from integral types.
The first three are tricky to detect, but the fourth is rather easy to double-check: just search for all casts between integrals and pointers and make sure they all make sense.
Except ... how do you find pointer casts? How do they differ from parenthesed expressions, from function declarations or function calls? In C, they don't: it's all a pair of parentheses. In C++, you know it's where the reinterpret_casts are. It's easy to search for.
4) Casts are bad. Every cast is a potential design problem. Some are unavoidable, some are just fine, but every single one must be considered carefully. Hopefully, writing the full operator makes you think harder about using the cast. Typing a pair of parentheses is just too easy.
5) Polymorphism. At the end of the day, the C-style cast simply cannot do what dynamic_cast does.
Rule of thumb: just because the compiler allows it, doesn't mean it's a good thing. The compiler allows you to write a 10000 line main() littered with gotos jumping all over the place. Most compilers allow you to call main() explicitly, although such a thing is forbidden by the standard. Look at the entries of the Obfuscated C contest for a taste of all the stuff that the compiler allows that is a really bad idea.
All the buzzt!
CornedBee
"There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
- Flon's Law
Thanks for very useful post - I think it is valueable enough to add to the FAQ
If I have eight hours for cutting wood, I spend six sharpening my axe.
Okay. I get it now.
Last edited by Queatrix; 12-23-2006 at 12:48 PM.
Hint: work != good. I second a FAQ addage(?).
I vote Excellent too.
dwk
Seek and ye shall find. quaere et invenies.
"Simplicity does not precede complexity, but follows it." -- Alan Perlis
"Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
"The only real mistake is the one from which we learn nothing." -- John Powell
Other boards: DaniWeb, TPS
Unofficial Wiki FAQ: cpwiki.sf.net
My website: http://dwks.theprogrammingsite.com/
Projects: codeform, xuni, atlantis, nort, etc.
Thanks![]()
All the buzzt!
CornedBee
"There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
- Flon's Law
Wow, doesn't know about that before. This is enlightening. Thanks.
ERROR: Brain not found. Please insert a new brain!
“Do nothing which is of no use.” - Miyamoto Musashi.
Excellent... and now the nit:
Code:void foo(const _TCHAR *txt) { legacy(const_cast<char*>(txt)); // Compiles only if _TCHAR is char. }![]()
I used to be an adventurer like you... then I took an arrow to the knee.
Yeah, that was a typo. I wrote everything in the post window. I didn't expect it to get so long![]()
All the buzzt!
CornedBee
"There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
- Flon's Law
Thread copied to the FAQ board and posts pruned for clarity; the minor edit suggested by hk_mp5kpdw has been applied. Please feel free to continue the discussion in this thread and suggest any amendments etc.
Thanks very much, CornedBee.
Thanks, too, to everyone who took the time to rate the thread.
CProgramming FAQ
Caution: this person may be a carrier of the misinformation virus.
Fantastic post, thank you very much.