Type Conversion Operators in C++


August 9, 2022, Learn eTutorial
1026

The type-cast operator has a specific syntax: it starts with the operator keyword, then the destination type, and an empty set of parentheses. The destination type is the return type, so it is not specified before the operator keyword. In this tutorial let us see how conversion is done by the cast operators in C++. A cast operator is a unary operator that converts one Data Type into another.

Four types of casting are supported by cast operators  in C++ and they are

  1. Static Cast
  2. Dynamic Cast
  3. Constant Cast
  4. Reinterpret cast

1.    Static Cast in C++

This is the most basic type of cast that can be used. It is a time cast that has been compiled. It performs implicit type conversions (such as int to float or pointer to void*) and can also invoke explicit conversion functions (or implicit ones).

The static cast can convert pointers to related classes, including not only upcasts (from pointer-to-derived to pointer-to-base) but also downcasts (which are from pointer-to-base to pointer-to-derived). During runtime, no checks are performed to ensure that the object being converted is a full object of the destination type. As a result, it is the programmer's responsibility to ensure that the conversion is safe. On the other hand, it does not incur the overhead of dynamic cast's type-safety checks.

Syntax


class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast<Derived*>(a);

Even though b would point to a class object that is incomplete and could cause runtime errors if dereferenced, this would still be a valid code.

As a result, static_ cast can perform not only the implicitly permitted conversions but also their inverse conversions with pointers to classes.

Static_ cast can also perform all implicit conversions (not just those involving pointers to classes), as well as the inverse of these. 
This can convert void* to any pointer type. In this case, it ensures that the resulting pointer value is the same if the void* value was obtained by converting from the same pointer type. Integers, floating-point values, and enum types can all be converted to enum types.

For an example

Example : Static Cast in C++


#include <iostream>
using namespace std;
int main()
{
    float f = 4.6;
    int a = f; 
    int b = static_cast<int>(f);
    cout << b;
}

 

Output:


4

2. Dynamic Cast in C++

Only pointers and references to classes, or void*, can be used with the dynamic cast. Its goal is to ensure that the type conversion result points to a valid complete object of the destination pointer type.

This naturally includes pointer upcast (converting from pointer-to-derived to pointer-to-base), in the same way, that implicit conversions are allowed.

However, the dynamic cast can downcast (it can convert from pointer-to-base to pointer-to-derived) polymorphic classes (those with the virtual members) it happens only if the pointed object is a valid complete object of the target type.

For Example,

Example : Dynamic Cast in C++


#include <iostream>
#include <exception>
using namespace std;

class Base { virtual void dummy() {} };
class Derived: public Base { int a; };

int main () {
  try {
    Base * pba = new Derived;
    Base * pbb = new Base;
    Derived * pd;

    pd = dynamic_cast<Derived*>(pba);
    if (pd==0) cout << "Null pointer is on the first type-cast.\n";

    pd = dynamic_cast<Derived*>(pbb);
    if (pd==0) cout << "Null pointer is on the second type-cast.\n";

  } catch (exception& e) {cout << "Exception: " << e.what();}
  return 0;
}

Output:


Null pointer is on the second type-cast.

The code above makes an attempt to perform  two dynamic casts from pointer objects of type Base* (pba and pbb) to pointer objects of type Derived*, but only the first seems to be successful. Take note of their initializations:


Base * pba = new Derived;
Base * pbb = new Base;
 

Although they both point to objects of type Base*, pba actually points to a Derived object, whereas pbb points to a Base object. As a result, when their respective type-casts are carried out using dynamic_ cast, pba points to an object of class Derived that is fully formed, whereas pbb points to an object of class Base that is only partially formed.

When the dynamic cast is unable to cast a pointer because it is not a complete object of the required class, as in the second conversion in the preceding example, it returns a null pointer to indicate the failure. If dynamic_  cast is used to convert to a reference type and the conversion fails, a bad_ cast exception is thrown instead.

The other implicit casts allowed on pointers are: casting null pointers between pointer types (even in between the unrelated classes), and casting any kind pointer of any type mainly to a void* pointer.

3.    Constant Cast in C++

This type of casting manipulates the consistency of the object pointed by a pointer, either setting or removing it.  To pass a const pointer to a function that will expect a non-const argument.

Example :Const Cast in C++


// const_cast
#include <iostream>
using namespace std;

void print (char * str)
{
  cout << str << '\n';
}

int main () {
  const char * c = "just a sample text";
  print ( const_cast<char *> (c) );
  return 0;
}

Output:


just a sample text

in the above example is guaranteed to work only because function print doesn't really write to the pointed object. It should be noted that removing the constness of a pointed object in order to write to it leads to an undefined kind of behavior.

4.    reinterpret_ cast in C++

reinterpret_cast converts any pointer type to any other pointer type, even if they are from different classes. The operation produces a straightforward binary copy of the value from one pointer to the other. 
It also has the ability to cast pointers to and from integer types. This integer value represents a pointer in a platform-specific format. The only guarantee is that a pointer cast to an integer type large enough to contain it completely (such as intptr t) can be cast back to a valid pointer is guaranteed.

Conversions that can be performed by reinterpret_ cast but not by static_cast are low-level operations that rely on reinterpreting the binary representations of the types, resulting in code that is system-specific and thus non-portable in most cases.

For Example,

class A { /* ... */ };
class B { /* ... */ };
A * a = new A;
B * b = reinterpret_cast<B*>(a);

This code compiles, but it makes no sense because b now points to an object of a completely unrelated and likely incompatible class. Dereferencing b is not all safe.