C++ casts

By , last updated September 25, 2019

Casting in C++ is converting one type of data into an other type of data.

There are a couple of legitimate use cases for casts, but most of them are a indicator of a bad design.

C++ parenthesis cast

The old type C-style cast with parenthesis is allowed in C++, but I would highly discourage it. Do not make a cast from int to short with parenthesis like that:

int low = 1500;
short lower = (short) low;

The reasoning behind it is because it will try anything to make the cast succeed, even the most damaging cast.

C++ static_cast

Use the static_cast construct instead of parenthesis if you’re able to. It’s more safe and it does some type checking and it always does the same. The (short) cast could possibly do harmful stuff to your program and computer when used wrong.

Simple C++ cast from integer to short should look like this:

int low = 1500;
short lower = static_cast<short>( low );

With static_cast, you tell the compiler and subsequent maintainers of your code that this cast was intentional. However, even if a cast is intentional, doesn’t mean it’s not a bug!

C++ dynamic_cast

The next type of cast is dynamic_cast. It’s function is to downcast and upcast to/from a base type to a type further down/up in the hierarchy.

Here is a C++ dynamic_cast example:

class A;
class B : public A;

// B inherits A
B *b = new B();

// Cast up to A
A *a = dynamic_cast<a*>( b );

// Cast down from A to B
B *bcast = dynamic_cast<B*>( a );

C++ reinterpret_cast

This is the big gun among the casts. If static_cast is an air-gun, this is an anti-ship gun. So you better be careful and really think twice before needing to use reinterpret_cast.

Reinterpret_cast takes some data and pretends it’s something else. In my experience, reinterpret_cast is good for casting from and to void*. For anything else, reinterpret_cast is bad.

With reinterpret_cast do essentially say to the compiler, that you, the programmer, know more than the C++ type system and that you take full responsibility when using reinterpret_cast.

Consider the following case. These are all allowed, enabled by reinterpret_cast. That it’s possible doesn’t mean it’s good or sane to do such things. But the possibility is there.

int a = 0;
float &b = reinterpret_cast<float&>(a);

This is an int reinterpret_cast to float. I’m not quite sure if this is legal C++. It may be in violation of strict aliasing rules. But during my tests the code didn’t do funny things. But just don’t make code like this unless you’re testing something or have very good reasons using this in production code. And if you do, check the C++ standard if your case is allowed or not.

The variable a and b points to the same memory area, but through different types. They share the same bits, but what the bits mean would be different.

The following example code and results are compiler and system dependent. If you can’t reproduce to the exact same numbers, it means the CPU and compiler are different than I have.

With the binary representation of 0 with a 32-bit int, the layout will be:

00000000 00000000 00000000 00000000

Luckily, both the int and float types are truly zero (0) when all bits are null. Printing both a and b will give “0” for both.

a: 0, b: 0

Things start to get fuzzy when you assign 1 to a, the int.

00000000 00000000 00000000 00000001

Again, the same binary number has a different representation regarding who’s looking. An int would say it’s 1, while a float says it’s a very small number

a: 1, b: 1.4013e-45

And it gets fuzzier when you assign 1 to b, the float.

With IEEE 754, the internal representation will be

00111111 10000000 00000000 00000000

The output is then a: 1065353216, b: 1.

This page will give you more details about IEEE 754, http://www.h-schmidt.net/FloatConverter/IEEE754.html.

Reinterpret_cast ignores memory boundaries

If the type you’re casting to is larger than the type casting from, tough luck. Your application may or may not explode when put into production and has been running “fine” for 1 month.

// Array with 4 chars
unsigned char c1[4] = { 0 };
int & char_alias = reinterpret_cast<int&>(c1);

If you assign a large enough value to char_alias, all 4 entries in the array will be modified, even though you only meant to modify the first.

char_alias = 1;         // modifies index 0
char_alias = 300;       // modifies index 0 and 1
char_alias = 70000;     // modifies index 0, 1 and 2

Thinking about using reinterpret_cast? Just don’t.

C++ const_cast

Const_cast removes the const modifier to a variable and is also troublesome. If you are in the need for const_cast, I’d say you have something wrong with the design of your application.

Const_cast use cases may also be used to remove the volatile specifier.

To cast away the const qualifier, cast to the type intended.

const int c = 0;
int & d = const_cast<int&>(c);

It is allowed, but undefined, to assign to the non-const reference d.

d = 1;

Printing both c and d, may yield identical or different results, depending on your compiler.

std::cout << "c: " << c << ", d:" << d << std::endl;

During my testing, I got c=0 and d=1.

However, when calling a method which takes a const reference to an int as input parameter, the c and d variables are identical in both MSVC 2015 and G++ 4.9.3.

void use_var(const int & var)
{
	std::cout << "var: " << var << std::endl;
}

const int c = 0;
int & d = const_cast<int&>(c);
d = 1;
std::cout << "c: " << c << ", d:" << d << std::endl;
use_var(c);

The method use_var(c) prints 1, while the std::cout prints 0 for c.

What to take home from const_cast? Just don’t do it.

Volatile cast

Casting away the volatile specifier is similar to casting away the const qualifier.

const volatile int cv = 0;
int & d = const_cast<int&>(cv);
d = 1

Again, just don’t do this. The must be a better way to achieve what you’re trying to do without using casts.

Professional Software Developer, doing mostly C++. Connect with Kent on Twitter.