C++ Type Conversion Operators

Using named casts is considered a safer and reliable method of type conversion in C++ compared to other methods. In C++, there are four main named type-casting expressions:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast

C++ static_cast

We use static_cast for standard type conversions, such as converting from float to int.

Let's look at an example.

#include <iostream>
using namespace std;

int main() {
    float my_float = 3.14;
    
    // convert float to int
    int my_int = static_cast<int>(my_float);
    cout << "Float: " << my_float << " -> Int: " << my_int << endl;

    return 0;
}

Output

Float: 3.14 -> Int: 3

The static_cast<int>(my_float) expression converts the float value stored in my_float to an integer and stores it in my_int.

Because floating-point values can have decimal components and integers cannot, this conversion removes the decimal part, converting 3.14 to 3.


C++ dynamic_cast

We mainly use dynamic_cast for polymorphic type conversions, especially when dealing with inheritance hierarchies. It's typically used in scenarios where a base class pointer needs to be converted to a derived class pointer.

Let's look at an example.

#include <iostream>
using namespace std;

class Base {
public:
    // base class print function
    virtual void print() {
        cout << "Base class" << endl;
    }
};

class Derived : public Base {
public:
    // derived class print function overriding the base class
    void print() override {
        cout << "Derived class" << endl;
    }
};

int main() {
    // create a Base class pointer 
    // pointing to a Derived object
    Base *base_ptr = new Derived();
    
    // use dynamic_cast to cast the 
    // Base pointer to a Derived pointer
    Derived *derived_ptr = dynamic_cast<Derived*>(base_ptr);
    
    // call the print function through the derived pointer
    if (derived_ptr) {
        derived_ptr->print();
    }

    // delete the dynamically allocated object
    delete base_ptr;

    return 0;
}

Output

Derived class

In this example,

  • We defined a base class Base and a derived class Derived, where Derived inherits from Base.
  • In main(), we created a pointer base_ptr of type Base* pointing to a Derived object.
  • We then used dynamic_cast to cast base_ptr to a Derived* pointer and assign it to derived_ptr.
  • Finally, we called the print() function through derived_ptr, which means that we can access the Derived class function through the Base class pointer after the dynamic cast.

Note: If the base class pointer doesn't point to an object of the derived class, dynamic_cast returns nullptr.


C++ const_cast

We use const_cast to cast away the const qualifier from a variable.

One common scenario where we can use const_cast is when working with third-party libraries that have functions which take non-const pointers as arguments, but we need to pass in const data.

Let's look at an example.

#include <iostream>
using namespace std;

// function that takes a non-const pointer
void modify_data(int* data) {
    // modify the data
    *data *= 2;
}

int main() {
    int x = 10;
    
    // a const pointer for variable x
    const int* ptr = &x;

    // use const_cast to 
    // remove const qualifier and allow modification
    int* mutable_ptr = const_cast<int*>(ptr);

    // call the function
    modify_data(mutable_ptr);

    // value is modified successfully
    cout << "Modified value: " << x << endl;

    return 0;
}

Output

Modified value: 20

In this example, we used the const_cast expression to remove the const qualifier from a pointer to pass it through a function that takes non-const pointer as an argument.

Here,

  • modify_data(int* data): is a function that takes non-const pointer as an argument
  • ptr: is a const pointer that we need to pass to modify_data
  • const_cast<int*>(ptr): casts ptr as a non-const pointer and assigns it to mutable_ptr

C++ reinterpret_cast

reinterpret_cast is used to convert one pointer type to another pointer type or one reference type to another reference type.

Unlike static_cast, reinterpret_cast doesn't actually convert the data types but reinterprets one pointer type as another at compile time.

Let's look at an example.

#include <iostream>
using namespace std;

int main() {
    // create an integer variable
    int x = 67;

    // pointer to an integer
    int* ptr_to_int = &x;

    // reinterpret the pointer to an integer 
    // as a pointer to char
    char* ptr_to_char = reinterpret_cast<char*>(ptr_to_int);

    // dereference the double pointer
    // originally holding an integer as if it contains a double
    cout << "Dereferencing ptr_to_char: " << *ptr_to_char << endl;

    return 0;
}

Output

Dereferencing ptr_to_char: C

Here, we used reinterpret_cast to reinterpret the pointer ptr_to_int, which originally pointed to an integer, as a pointer to a character.

This means that the memory location pointed by ptr_to_int still holds an integer value. But it will be treated as a character.

Warning: reinterpret_cast allows for almost any pointer or integer type conversion without any type safety checks. This can result in undefined behavior.

So, reinterpret_cast should be used with caution.

Did you find this article helpful?

Your builder path starts here. Builders don't just know how to code, they create solutions that matter.

Escape tutorial hell and ship real projects.

Try Programiz PRO
  • Real-World Projects
  • On-Demand Learning
  • AI Mentor
  • Builder Community