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
expressionindynamic_castshould be a pointer or a reference - AND the type (class) must be polymorphic (it must include a
virtualfunction), 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
- C++ Crash Course (Josh Lospinoso) — 6. Compile-Time Polymorphism
- Explicit type conversion — https://en.cppreference.com/w/cpp/language/explicit_cast
- 9.2.9.3 Simple type specifiers [dcl.type.simple] — https://eel.is/c++draft/dcl.type.simple#nt:simple-type-specifier
- 8.5 — Explicit type conversion (casting) and static_cast — https://www.learncpp.com/cpp-tutorial/explicit-type-conversion-casting-and-static-cast/
- const_cast Operator — https://docs.microsoft.com/id-id/cpp/cpp/const-cast-operator?view=msvc-160
- dynamic_cast Operator — https://docs.microsoft.com/id-id/cpp/cpp/dynamic-cast-operator?view=msvc-160
- static_cast Operator — https://docs.microsoft.com/id-id/cpp/cpp/static-cast-operator?view=msvc-160
- Can't downcast because class is not polymorphic? — https://stackoverflow.com/questions/8469900/cant-downcast-because-class-is-not-polymorphic