What's the fuss about?

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 ...

  1. #1
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342

    Thumbs down What's the fuss about?

    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?

  2. #2
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    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:
    Code:
    void foo(double *val)
    {
      int v = *val;
    }
    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 = (int)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.
    C++-style, you would have done this:
    Code:
    void foo(double *val)
    {
      int v = static_cast<int>(val);
    }
    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.
    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.)
    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.
    }
    _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.
    OK, once again, you solve this with a C-style cast:
    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);
    }
    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:
    #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);
    }
    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.
    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:
    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.
    }
    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.

    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

  3. #3
    CSharpener vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,484
    Thanks for very useful post - I think it is valueable enough to add to the FAQ
    The first 90% of a project takes 90% of the time,
    the last 10% takes the other 90% of the time.

  4. #4
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342
    Okay. I get it now.
    Last edited by Queatrix; 12-23-2006 at 12:48 PM.

  5. #5
    MFC killed my cat! manutd's Avatar
    Join Date
    Sep 2006
    Location
    Boston, Massachusetts
    Posts
    870
    Hint: work != good. I second a FAQ addage(?).
    Silence is better than unmeaning words.
    - Pythagoras
    My blog

  6. #6
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,047
    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.

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    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

  8. #8
    In the Land of Diddly-Doo g4j31a5's Avatar
    Join Date
    Jul 2006
    Posts
    476
    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.

  9. #9
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,803
    Excellent... and now the nit:

    Code:
    void foo(const _TCHAR *txt)
    {
        legacy(const_cast<char*>(txt)); // Compiles only if _TCHAR is char.
    }
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    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

  11. #11
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    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.

  12. #12
    Registered User
    Join Date
    Dec 2006
    Posts
    17
    Fantastic post, thank you very much.

Popular pages Recent additions subscribe to a feed

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21