C++ Succinctly: Casting in C++

Overview

There are five different ways of casting variables. There is overlap between them, especially with the C-style cast and all other casts, but each has its use. It is good to learn them all, so you can use the best cast for your particular need rather than using any cast that happens to work. If you ever need a quick reference, I recommend this post on StackOverflow.

I’m not discussing implicit casting here for the simple reason that it’s a basic concept with an almost infinite number of variations. If I write float f = 10; I’ve implicitly cast an integer to a float and stored its result in f. You can also implicitly cast an object of type B to a pointer to its base class A using the address-of operator or to a reference to its base class A by doing a normal assignment.

const_cast

The const_cast operator can add and remove const and volatile. Using it to add either of these attributes is fine. It’s rare that you would, but if you do, you can.

Its ability to remove const is something you should never use in a C++ program except when you need to call a C-language function that doesn’t abide by const-correctness but does not modify the object at all. If a function has a const parameter, and throws out the const-ness of it by using const_cast, the function is breaking the implied contract that it will not modify the parameter. So it’s up to you as the author of that function to ensure that you are not going to modify the object; otherwise, you should not use const for the parameter since you will be modifying the object.

If you ever need to use const_cast and another cast operator on the same object, use const_cast last, since removing const-ness from an object could allow unintended changes to take place if you used a subsequent cast.

static_cast

The static_cast operator is useful for casting:

  • Floating point types to integer types (producing a truncated result)
  • Integer types to floating point types.
  • Enum types to integer types.
  • Integer types to enum types.
  • Derived classes to base classes.
  • A types to a derived type reference.
  • A derived class pointer to a base class pointer.

In general, whenever casting fundamental types to other fundamental types, use static_cast. Typically, static_cast should be your first choice of casts, as it does all the checking it can do at compile-time, so you don’t have added run-time checking to slow down your program.

dynamic_cast

The dynamic_cast operator is useful for casting through virtual inheritance. static_cast can cast from a derived class to a base class, whether the inheritance is virtual or not. Say, however, you are given an object of type A, but you know it is actually an object of type B—and that B inherits virtually from A. If you want to cast this object back to B to use member functions that only B provides, you need to use dynamic_cast.

A few things about dynamic_cast. First, it only works on pointer-to-pointer or reference-to-reference conversions. Second, it can’t actually cast an object from an A to a B if the object is not, in fact, a B (or of a type derived from B). A pointer-to-pointer dynamic_cast that fails returns null. A reference-to-reference failure throws a std::bad_cast exception.

reinterpret_cast

The reinterpret_cast operator is a direct conversion with very few good uses. Most its operations give undefined results. What this means in practice is that you should read the compiler vendor’s documentation before using it for anything.

One use for it, as we saw in StorageDurationSample, is to cast a pointer to an integer type large enough to hold it. This gives the memory address of the pointer, which can be useful for debugging and tracing operations where you can dump data to log files and create core dumps, but it may not be able to run a debugger easily. You will see it used legitimately at times for other purposes, but in general, it should be considered as the cast of last resort (excluding a C-style cast, which comes after reinterpret_cast).


C-style Cast

The C-style cast, (e.g., auto someData = (SomeType)dataOfSomeOtherType;) is not your friend. You are undoubtedly familiar with it from C#, where it is highly useful. In C#, if you try to make a cast using that syntax, and the cast is invalid, you will produce an InvalidCastException. This happens because the CLR keeps track of the types of everything you’ve created and detects bad casts.

C++ does not check to see if your C-style cast is valid, assuming it compiles, of course. C++ just assumes that it is. If it’s a bad cast, and you are lucky, your program will crash immediately. If not, you will end up with data in an unknown state, which will certainly become corrupted in subtle and insidious ways.

Further, unlike the other casts, which you can easily spot by looking for _cast<, C-style casts do not stick out. When you’re scanning lots of code quickly, parentheses wrapped around text looks as much like a function call as it does a cast operation. You could use a regular expression search for this in Visual Studio 2012: \(.*\)[A-Za-z]. Even so, you are still forgoing all the benefits and protections of the other casts.

The one thing a C-style cast can do that other casts cannot is cast an object to one of its protected or private inheritance base classes. You really shouldn’t do this since, if you need public inheritance, you should use public inheritance.

In short, don’t use C-style casts.

Sample

There is a sample, CastingSample, that demonstrates the many possible types of casting. It is included with the source code for this series. In the interest of brevity, I am omitting it here.

Conclusion

In this article, we've covered casting in C++ and I hope it's clear that you shouldn't use C-style casts. The next article zooms in on strings in C++.

This lesson represents a chapter from C++ Succinctly, a free eBook from the team at Syncfusion.
Tags:

Comments

Related Articles