Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Why I Hate C++ Cast Operators

Last updated Jan 1, 2003.

It took me awhile to admit that C++ cast operators are nothing but a major annoyance. Their alleged advantages compared to C-style cast are negligible, if any, whereas the damage and the confusion that they cause are much more significant that people realize. Compared to other design mistakes in the evolution of C++ such as namespaces, exception specifications and exported templates, C++ cast operators are the most harmful. Here’s why.

Readability and Explicitness

The C++ style cast operators are a classic example of over-engineering and design by committee. Why was it necessary to add three new operators with ugly names, silly underscores and heinous syntax to a language that already had a pretty reasonable cast notation?

According to the official C++ catechism, casts in general are dangerous and unwelcome. The C-style cast notation is even worse because it isn’t explicit enough. The programmer can’t tell whether the cast expression performs a relatively safe or it’s an inherently dangerous cast.

Experience with C++ cast operators consistently shows that programmers don’t want to be bothered with these minutiae, though. In fact, many of them don’t quite understand what the difference is between a static cast and a reinterpret cast operations anyway. They prefer to let the implementation do the right thing instead of splitting hairs. And they are right. How many programmers understand how the dynamic resolution of virtual functions is implemented? Imagine how annoying it would be if the programmer had to use a different syntactic form for calling a virtual function as opposed to calling a non-virtual function. As silly as it may sound, that’s exactly what the C++ cast operators force to do – poke into the intricacies of their implementation.

There’s another reason why C++ pundits scorn C-style cast -- the same cast expression may perform two conversions at once:

const unsigned int *lp; //ptr to const unsigned int
(int *) lp; //get rid of unsigned and const at once.

And I say: so what? If the programmer made a mistake here, that’s too bad. Then again, programmers make much more mistakes by confusing delete and delete[]. Besides, what if the programmer had truly meant to use two cast operations in one expression? Should the programming language twist his arm and force him to use two different cast expressions? Would that make the code clearer or safer in anyway? Perhaps the following example will convince you that it certainly wouldn’t:

reinterpret_cast <int *> (const_cast<unsigned int *>(lp))//instead of (int *) lp

So much for clarity and explicitness!

Having to spell out the difference between static cast and reinterpret cast is laborious and bothersome but still makes some sense. Having to use a special cast operator to remove (or add) const/volatile is plain dumb. Why not invent a special cast operator that adds or removes only the unsigned property? What about a special cast operator for pointers only? Every time you convert an integer to a pointer or vice versa, you will have to use a special ptr_cast expression. I can go on with these preposterous ideas -- pointer_to_function_cast anyone?

What good is static_cast anyway?

In his FAQ page, Bjarne Strosutrup addresses a question that is muttered silently among C++ students -- what good is static_cast? In spite of all the verbal stunts and pedagogy, there’s no escape from admitting that static_cast is really useless. Almost every static_cast expression is redundant since the conversion would take place even if you didn’t use that operator at all. Examples of such redundant casts include:

  • Conversion of base classes to derived classes, including pointers and references
    Derived *p= static_cast<Derived *> (pbase); 
  • Conversions from one numeric type to another:
    int result= static_cast<int>(12.56);
  • Conversion to void*:
    void*opaque= static_cast<void*> (&myobj);

Stroustrup concludes: "because static_cast is so ugly and so relatively hard to type, you’re more likely to think twice before using one". I agree. Programmers will not use static_cast -- they will simply let the code compile without an explicit cast operator at all:

Derived *p= pbase; 
int result= 12.56; //yes, this code compiles
void*opaque =&myobj;

The New Cast Operators are Dangerous

The C++ cast operators aren’t just a burdensome nuisance. They’re also dangerous and could result in bugs that are hard to find. Ironically, these bugs are exactly what the artificial split between static_cast and reinterpret_cast was meant to avoid!

Suppose a new C++ programmer has to cast a pointer to a function to a variable of type long long using C-style cast:

void myfun(bool) 
{}
void (*pf) (bool) = myfun;
long long addr= (long long) myfunc;

This code compiles and produces the expected results on most modern platforms. Now suppose that that poor programmer is reprimanded for using C-style cast. She is told to replace the C-style cast expression with the allegedly better and safer C++ cast operators. Should she use static_cast or reinterpret_cast? The "recommended" approach is to start with static_cast and see if it works:

long long val= static_cast<long long> (myfunc); //compilation error

As said earlier, static_cast is useless. So she replaces that operator with reinterpret_cast:

long long addr= reinterpret_cast<long long> (myfunc);

Now the code compiles and works as it should. Inspired by her success with reinterpret_cast, our poor programmer decides to give up static_cast completely and stick to reinterpret_cast exclusively. The next day, she has to convert a float to int. Knowing that she has to use C++ cast operators, and having learned that static_cast is virtually useless, she decides to do the following:

float d=15.95;
int n= reinterpret_cast<int> (d);// compilation error

This code doesn’t compile. As a last resort, the programmer uses the following:

int n= *reinterpret_cast<int*> (&d);

This code compiles without a problem. Our programmer expects that after the cast operation, n shall be 15. Alas, it’s actually 1717986918! The reasons for this aren’t very important at the moment; what’s important is that many C++ programmers do believe that the cast expression above should assign 15 to n, which it doesn’t. Lo and behold, C-style cast does get it right:

double d=15.95;
int n= (int) d; // n=15

This is because C-style cast is context sensitive. In this context, it knows that the programmer wanted to convert the value 15.95 -- not the underlying bit pattern -- to int. Paradoxically, the new cast operators are evolutionary step backward. They force the programmer to know perhaps too much about the underlying machinery of C++. Under these circumstances programmers usually opt for one of the following options: use whatever they think will work and pray for good, or invest precious time in learning what the subtle differences between static cast and reinterpret cast. In real world programming, your team will have at least some programmers of the first category.

Naming Names

The new cast operators are intentionally ugly and long. However, the belief that their ugliness would deter programmers from using casts was naive. The ugliness deters programmers from using the ugly cast operators. It doesn’t dissuade them from using casts. There’s another side to this story. Programmers use casts not because they’re fun but because they are needed, or at least considered as needed. If a programming language punishes programmers for using features that aren’t avoidable, don’t be surprised to find more bugs and dragons in C++ code.

There’s one argument against C-style cast which sounds convincing. The C-style cast syntax isn’t easily recognizable by text processors and code analysis tools. If you want to find every occurrence of a C-style cast expression in a large source file, you can’t just type "find ’)’". And yet, two facts weaken this claim considerably:

  • Many conversions in a C++ program are implicit, and don’t use any cast operator
  • Searching for C++ cast expressions is still a pain because you have to look for at least three different tokens: reinterpret_cast, static_cast and const_cast

By "at least three different tokens" I mean that programs using C-style cast operators might still use function style cast (which is unique to C++) and C-style cast all at once. Furthermore, because programmers don’t always know which C++ cast operator is needed for the task, you must examine every C++ cast expression closely and check the types of the arguments carefully.

In Conclusion

After more than a decade of brain washing, I’m still not convinced that C++ cast operators really solve any problem. However, I am pretty convinced that they introduced many new problems and that they certainly didn’t live up to their expectations. Today it’s too late to get rid of them. Exception specifications and export were sinister mutations in the evolution of C++. However, they are pretty much extinct anyway, so the damage isn’t that great. Unfortunately, C++ cast operators are the worst of both worlds: they were a bad idea to begin with, and they are widely used today. The damage they have caused is enormous. The least I would expect is that C++ gurus, compiler warnings and textbooks would stop shoving them into our throats, telling us how wonderful they are.