Explicit Conversion (Casting)

Explicit conversion (aka. type casting) explicitly converts one type into another type (Lospinoso).

Note that type conversion produces a new value of the desired type and DOES NOT change the value or type of the object being converted (learncpp.com).

C-style casts

In standard C programming, casting is done using the () operator. So we use something like (type)my_object or (type)(my_object):

int main() {
  int a = 5;
  int b = 4;

  // Without casting, dividing integer with integer
  // results in integer (wrong result). Se we have
  // to convert either `a` or `b` into `double` to
  // get a floating-point result.
  double res = (double)a / b;
  printf("%lf\n", res); //=> 1.250000
}

Under the hood, C-style casts can do static_cast, const_cast, or reinterpret_cast depending on context. To always get the expected behavior, you should use named casts (explained below).

Functional casts

Use type(my_object) or type{my_object} (since C++11).

int main(int argc, const char * argv[]) {
  int value = 4;
  double casted = double(value);
  printf("%lf\n", casted); //=> 4.000000
}

They are equivalent to C-style casts, but you can only use a "simple type specifier" (one-word types like int or unsigned, but not unsigned int or int*) or a "typedef specifier" (cppreference.com).

Named casts

static_cast

static_cast<type>(expression) converts the value in the given expression as the given type.

int main() {
  int number = 123;
  printf("%lf\n", static_cast<double>(number));
  //=> 123.000000
}

It can also be used for casting a base class reference back to the original derived class reference (so you can access the derived class members):

struct Base {};

struct Derived : Base {
  int number;
};

int main() {
  // The first braze is to initialize the base class
  Derived d = { {}, 555 };

  // We're using a pointer and a reference as example here because
  // they work right away. If we use `Base` and `Derived` types
  // instead, we will need to implement the conversion function:
  // "No matching conversion for static_cast from 'Base' to 'Derived'"
  Base* b_ptr = &d;
  Base& b_ref = d;
  printf("%d\n", static_cast<Derived*>(b_ptr)->number); //=> 555
  printf("%d\n", static_cast<Derived&>(b_ref).number); //=> 555
}

dynamic_cast

dynamic_cast<type>(expression) works like static_cast, but performs run-time check to see if the casting is valid. This is safer than static_cast, but incurs an overhead.

  • The expression in dynamic_cast should be a pointer or a reference
  • AND the type (class) must be polymorphic (it must include a virtual function), because the compiler needs this for run-time type information.
struct Base {
  virtual ~Base() {}
};

struct Derived : Base {};

int main() {
  Base* base_ptr_1 = new Base{};
  Base* base_ptr_2 = new Derived{};

  // `dynamic_cast<Derived*>(base_ptr_1)` is not a valid cast
  // (casting a `Base*` object as `Derived*` type)
  // it will return the null pointer
  Derived* derived_ptr_1 = dynamic_cast<Derived*>(base_ptr_1);
  Derived* derived_ptr_2 = dynamic_cast<Derived*>(base_ptr_2);
  printf("%p\n", derived_ptr_1); //=> 0x0
  printf("%p\n", derived_ptr_2); //=> 0x1007af2d0
}

const_cast

const_cast<type>(expression) removes the const property from the given reference, pointer-to-object, or pointer-to-data-member in expression:

  • for pointers and references, it will refer to the original object
  • for pointers to class data members, it will refer to the original (uncast) pointers to class data members
struct Number {
  int number;
  // Notice the `const` member function.
  // Will cause compile error if we simply do
  // `number++` or `this->number++` inside.
  void increment() const {
    // We can instead get a non-const pointer
    // to the original class data member
    const_cast<Number*>(this)->number++;
  }
};

// Notice the const reference parameter
// Will cause compile error if we simply do
// `number_ref++` inside.
void incrementInt(const int& number_ref) {
  const_cast<int&>(number_ref)++;
}

int main() {
  Number n = { 10 };
  n.increment();
  printf("%d\n", n.number); //=> 11

  int a = 200;
  incrementInt(a);
  printf("%d\n", a); //=> 201
}

You cannot use const_cast to remove the const property of a constant variable:

const int a = 111;

int main() {
  int* b = const_cast<int*>(&a);
  // Will cause runtime error below
  // (because `b` refers to `a`, a constant variable,
  // and you are trying to change a CONSTANT)
  *b = 222;
}

reinterpret_cast

reinterpret_cast<type>(expression) interprets the value stored at address expression as the pointer-type type.

Commonly used in low-level programming (e.g. in embedded systems).

int main() {
  // E.g. if you store an `unsigned long*` value in memory address 0x1000
  unsigned long* timer_value = reinterpret_cast<unsigned long*>(0x1000);
  printf("Timer is %lu\n", *timer_value);
}

References