Introduction: why type casting in C++ is a minefield
\n\nHave you ever written something like (int)someFloat and hoped everything would be fine? Or maybe you've encountered a situation where the program compiles but crashes at runtime with a mysterious error? Welcome to the world of type casting in C++ — a place where even experienced developers lose hours debugging.
The question "Regular cast vs. static_cast vs. dynamic_cast" on Stack Overflow has gathered tens of thousands of views. And this is no coincidence. Each type of cast is a tool with its own purpose, and using the wrong tool can lead to undefined behavior, memory leaks, or simply non-working code.
\n\nIn this article, we will break down the three main ways of type casting in C++, show the most common errors, and give clear recommendations on how to avoid them. By the end, you will know exactly which cast to use in each specific situation.
\n\n1. Regular cast (C-style cast) — a legacy of the past
\n\nThe good old C-style cast is the simplest, yet the most dangerous way of casting. It looks like this: (NewType)expression. In C++, it is considered "regular," but its behavior can be very surprising.
How does it work?
\nThe C++ compiler tries to interpret the C-style cast as a sequence of static_cast, const_cast, and reinterpret_cast. It selects the first suitable combination. The problem is that you never know exactly which cast will end up being applied.
Example of an error: accidentally changing constness
\nconst int value = 42;\nint* ptr = (int*)&value; // C-style cast removes const\n*ptr = 100; // Undefined behavior!\ncout << value; // May output 42, 100, or anything\n\nThis code compiles without errors but leads to undefined behavior. const_cast would be more honest here — it at least shows your intention.
When is C-style cast still acceptable?
\n- \n
- Never in modern C++ (C++11 and newer). \n
- Only for very simple numeric conversions where the risk is minimal (e.g.,
(float)intValue), but even then it's better to usestatic_cast. \n
Conclusion: forget about C-style cast. It makes code unreadable and unsafe. Modern compilers may even issue warnings for such constructs.
\n\n2. static_cast — the universal soldier
\n\nstatic_cast is the most commonly used type of cast in C++. It performs a compile-time check and is suitable for most situations where the type conversion is logically correct.
When to use static_cast?
\n- \n
- Converting numeric types:
double→int,int→float. \n - Upcast (casting a pointer to a derived class to a pointer to a base class). \n
- Explicitly converting
void*back to a specific type. \n - Calling constructors with a single argument (but it's better to use
explicit). \n
Example: correct upcast
\nclass Base { public: virtual void speak() { cout << "Base"; } };\nclass Derived : public Base { public: void speak() override { cout << "Derived"; } };\n\nDerived d;\nBase* b = static_cast<Base*>(&d); // upcast — safe\nb->speak(); // Outputs "Derived" thanks to the virtual function\n\nTop error #1: Slicing when passing by value
\nOne of the most insidious errors is slicing. It occurs when you cast an object of a derived class to a base class by value, rather than by reference or pointer