Static Casting
Explore the concept of static casting in C++, including examples and best practices for converting data types at compile time
We've previously seen how our data types can be converted to other types automatically. For example, we can use an int
in a location where a float
is expected, and our int
is just automatically converted to the equivalent float:
float MyNumber { 4 };
These are called implicit conversions, because we don't need to write any additional code to make them happen.
We also have the option, and sometimes the need, to perform explicit conversions. This involves writing some additional code, making it clear that we're performing a conversion, and what we're converting our expression to. Explicit conversions are referred to as type casting, or just casting.
Static Casting with static_cast
C++ offers many different styles of casting. The two most common forms we need are static casting which is done at build time, and dynamic casting which is done at run time. We'll look at dynamic casting later in the course.
Because static casting lets us convert our data at build time, this means it has two useful properties:
- There is a minimal performance impact, and often no performance impact at all
- The compiler can check if the cast is possible and throw an error if not, preventing us from introducing bugs
To convert the integer 5
to a float
, we can do this:
static_cast<float>(5)
To convert the double 5.0
to an int
, we can do this:
static_cast<int>(5.0)
More generally, if we want to convert SomeExpression
into anSomeType
, the pattern looks like this:
static_cast<SomeType>(SomeExpression)
SomeExpression
is anything that contains or returns data of type SomeType
, or data that can be converted into data of SomeType
.
The expression can be a literal value, as shown above; it might be a variable, it might be a call to a function; it might be a maths operation, and so on.
Here are some examples:
static_cast<int>(20.0f);
static_cast<int>(25.0);
static_cast<int>(25.0 / 5);
float SomeFloat { 10.f };
static_cast<int>(SomeFloat);
bool SomeBoolean { true };
static_cast<int>(SomeBoolean ? 20 : 50.0 );
double SomeFunction() { return 1.0; }
static_cast<int>(SomeFunction());
Equally, the thing that is returned from a call to static_cast<int>
is an integer, and can be used anywhere that an integer is valid:
// Saving the result to a variable
int MyInt1 { static_cast<int>(20.f) };
int MyInt2 { static_cast<int>(25.0) / 5 };
int Add(int x, int y) { return x + y; }
// Passing the result to a function
Add(
static_cast<int>(1.f),
static_cast<int>(2.0)
);
These examples are heavily focused on using static_cast
to convert to an int
, but it can be used on any data type, including user-defined types:
static_cast<float>(5.0);
static_cast<Vector3>(142);
static_cast<Monster>(5);
Static casting can also be used when working with pointers. We'll see that in more detail later in this chapter when we cover run-time polymorphism.
Test your Knowledge
Using static_cast
After running the following code, what is the returned by the highlighted line?
bool IsDouble(double Number) { return true; }
bool IsDouble(int Number) { return false; }
IsDouble(static_cast<int>(5.0));
C-Style Casting
Another style of casting that is sometimes used is C-style casting. It has a slightly simpler syntax but, for reasons we'll cover later in this section, we should avoid using it.
If we had the following static_cast
code:
static_cast<int>(5.0);
static_cast<bool>(SomeVariable);
static_cast<Character>(SomeFunction());
Using C-style casting, it would look like this:
(int)5.0;
(bool)SomeVariable;
(Character)SomeFunction();
Test your Knowledge
Using C-Style Casts
After running the following code, what is returned by the highlighted line?
bool IsDouble(double Number) { return true; }
bool IsDouble(int Number) { return false; }
IsDouble((int)5.0);
C style casting was, predictably, inherited from the C language.
The original C language only had one form of casting, which was used to cover all use cases. C++ has several options, each covering a specific use case where casting is useful.
The C++ casts are identified by names like static_cast
, dynamic_cast
, and reinterpret_cast
These modern operators, sometimes called named casts, should be preferred over c-style casts. They clarify exactly what we're doing, and they perform additional checks to ensure what we're doing makes sense for that use case.
For example, if we attempt to cast the literal "Hi"
to a number, that's almost certainly an error on our part. static_cast
alerts us that this conversion doesn't make sense when we try to compile our code:
#include <iostream>
using namespace std;
int main(){
cout << static_cast<int>("Hi");
}
error: 'static_cast': cannot convert from
'const char [3]' to 'int'
There is no context in which this conversion
is possible
The equivalent C-style cast performs no such checks - it lets our program compile, and leaves us with unpredictable behavior:
#include <iostream>
using namespace std;
int main(){
cout << (int)"Hi";
}
2041494544
Summary
In this lesson, we covered:
- Static casting is a type of explicit conversion performed at build time, allowing for type conversion with minimal or no performance impact.
- The syntax
static_cast<SomeType>(SomeExpression)
is used for converting one data type to another, and it can be applied to literals, variables, function calls, and more. - Static casting can check the conversion's validity at compile time, reducing runtime errors.
- While static casting is commonly used for primitive types, it's also applicable to custom types and pointers. We'll cover the pointer scenario later in the chapter.
- C-style casting, inherited from C, is simpler but less safe than static casting. It lacks compile-time checks which can lead to us accidentally introducing unpredictable behavior.
Virtual Functions and Overrides
This lesson provides an introduction to virtual functions and overrides, focusing on their role in enabling runtime polymorphism