The typecasting is the feature which makes C++ more type-safe, robust & may convince you to use it over C. But this is also a more underrated topic when you are a newbie or moving from C background. Hence, I come up with an article on it. Here, we will not only see the C++ type casting with example for C developers but we will also cover Why do we need typecasting? & C++ type casting cheat codes for C developers to remember & employ it easily.
Although I am not an expert but this is what I have learned so far from various sources & 5+ yrs of industry experience.
In C++, there are 5 different types of casts: C-style casts, static_cast, const_cast, dynamic_cast, and reinterpret_cast.
I usually start with “Why we need it?”, but this time first we quickly go through some jargons & I will end this article with some of CPP core guidelines on typecasting.

Jargons you need to face

  1. Implicit conversion: where the compiler automatically typecast. Like float f = 3;, here compiler will not complain but directly transform 3 which is of type integer into float & assign to f.
  2. Explicit conversions: where the developer uses a casting operator to direct the conversion. All types of manual casting fall under the explicit type conversions category. Like int * p = (int*)std::malloc(10);, here we explicitly casting void* to int*.
  3. l-value: an identifier which represents memory location. For example, variable name, *ptr where ptr points to a memory location, etc.
  4. r-value: a value which is not l-value, r-value appear on the right-hand side of the assignment(=) operator. Like
int a = 5; // 5 = r-value, 
q = p + 5; // p + 5 is r-value
Note: Although there are some exceptions & more to learn on lvalue, rvalue and their references in C++

Why do we need typecasting?

class A{};
class B{};
int main ()
{
  B b;
  A a = b; 
  return 0;
}
exit status 1
error: no viable conversion from 'B' to 'A'
  A a = b;
    ^   ~
note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to 'const A &' for 1st argument
class A{};
      ^
note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'A &&' for 1st argument
class A{};
      ^
1 error generated.
class B {
public:
  operator A(){
    cout<<"CONVERSION OPERATOR\n";
    return A();
  } 
};

5 C++ type casting with example for C developers

1️ C-style casts 

int main() { 
    float res = 10 / 4;
    cout<<res<<endl;
    return 0; 
}
float res = (float)10 / 4;
float res = float(10) / 4;

2️
static_cast 

int * p = std::malloc(10);
exit status 1
error: cannot initialize a variable of type 'int *' with an rvalue of type 'void *'
  int * p = std::malloc(10);
        ^   ~~~~~~~~~~
1 error generated.
int * p = (int*)std::malloc(10);
int * p = static_cast<int*>(std::malloc(10));
class B {};
class D : public B {};
class X {};

int main()
{
  D* d = new D;
  B* b = static_cast<B*>(d); // this works
  X* x = static_cast<X*>(d); // ERROR - Won't compile
  return 0;
}

3️
const_cast

1. Ignore constness
int i = 0;
const int& ref = i;
const int* ptr = &i;

*ptr = 3; // Not OK
const_cast<int&>(ref) = 3;  //OK
*const_cast<int*>(ptr) = 3; //OK
2. Modifying data member using const this pointer
class X
{
public:
    int var;
    void changeAndPrint(int temp) const
    {
        this->var = temp;                    // Throw compilation error
        (const_cast<X *>(this))->var = temp; // Works fine
    }
    void changeAndPrint(int *temp)
    {
        // Do some stuff
    }
};

int main()
{
    int a = 4;
    X x;
    x.changeAndPrint(&a);
    x.changeAndPrint(5);
    cout << x.var << endl;
    return 0;
}
3. Pass const argument to a function which accepts only non-const argument
int fun(int* ptr) 
{ 
    return (*ptr + 10); 
} 

int main(void) 
{ 
    const int val = 10; 
    cout << fun(const_cast <int *>(&val)); 
    return 0; 
} 
4. Castaway volatile attribute

4️
dynamic_cast

Base* CreateRandom()
{
    if( (rand()%2) == 0 )
        return new Derived1;
    else
        return new Derived2;
}

Base* base = CreateRandom();
Derived1 *pD1 = dynamic_cast<Derived1 *>(base);
if (pD1){
    pD1->Method1();
}

5️
reinterpret_cast

error: static_cast from 'int *' to 'uintptr_t'
      (aka 'unsigned long') is not allowed
        uintptr_t ptr = static_cast<uintptr_t>(p);
                        ^~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
uintptr_t ptr = reinterpret_cast<uintptr_t>(p);

Cheat code for C developers moving to C++ on type casting

After reading all this you may confuse on what to use & when! That's why I have created this cheat code
Note: 
const_cast
 and 
reinterpret_cast
 should generally be avoided because they can be harmful if used incorrectly. Don't use it unless you have a very good reason to use them.

Some of C++ core guidelines