You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In C++, conversion and casting refer to the mechanisms used to convert a
value from one data type to another by receiving an input value to generate a
target value.
Conversion can occur implicitly or explicitly.
Implicit conversion, also known as type coercion, happens
automatically when the compiler converts one data type to another
without requiring explicit instructions from the programmer.
In contrast, explicit conversionrequires the programmer to define
the conversion process, which can be accomplished through various casting
methods.
Conversion and casting can impact performance, especially in
performance-critical applications.
When a conversion occurs between two types, the impact on performance
depends on how difficult it is to operate on those types.
For example, converting between int and float can impact performance
because operations on float are generally slower.
However, converting between int and unsigned int does not typically have
a significant performance impact, as both are integer types.
Implicit Conversion
Explanation
Implicit conversion occurs when the compiler automatically converts a value
from one type to another, usually in situations involving mixed-type
expressions.
It is only allowed to happen once and happens automatically.
The compiler ensures that the conversion is valid and safe.
However, implicit conversions can sometimes lead to unexpected behavior,
especially if there’s data loss (e.g., converting a double to an int
might lose the fractional part).
When an object is copied using the assignment operator, if the
source object is of a different type, implicit conversion may occur,
invoking the assignment operator overload and potentially a
constructor.
Allowed implicit conversion:
Any pointer -> void pointer;
Between primitive types;
Derived classes -> Base classes;
Derived class pointers -> Base class pointers;
Derived class references -> Base class references;
const objects -> non-const objects;
Arrays or { ... } -> std::initializer_list;
Examples
classB {
public:explicitB( Type mem ): _mem( mem ){};
Type _mem;
};
classA {
public:A(): _mem( initializer1 ) {};
// Constructor for implicit conversion.A( const B& b ): _mem( b._mem ) {};
// Assignment operator overload.
A& operator=( const B& b ) {
_mem = b._mem;
return *this;
};
// Assignment operator overload.
A& operator=( const A& other ) = default;
Type _mem;
};
intmain() {
A a; // Default A.
B b( initializer2 ); // B initialized with an initializer2.// Only A's assignment operator are invoked. In fact, it's also a implicit conversion.
a = b;
};
classB {
public:explicitB( Type mem ): _mem( mem ){};
Type _mem;
};
classA {
public:A(): _mem( initializer1 ) {};
// Constructor for implicit conversion.A( const B& b ): _mem( b._mem ) {};
// Assignment operator overload.
A& operator=( const A& other ) = default;
Type _mem;
};
intmain() {
A a; // Default A.
B b( initializer2 ); // B initialized with an initializer2.// First, implicit conversion: A's conversion constructor is invoked.// Second, assignment: A's default assignement operator is invoked.
a = b;
};
Explicit Conversion (Type Casting)
Explanation
Explicit conversion, or type casting, allows programmers to
define how a value should be converted from one type to another.
This is crucial in scenarios where implicit conversion may lead to data loss
or unintended behavior.
However, there’s no guarantee that the conversion is safe, and
programmers must ensure the correctness.
C-style Casting (Recommend for Performance)
Explanation
C-style casting uses the syntax (Type)initializer to convert a variable or
a value to the desired type.
While this method is straightforward, it can be ambiguous and may
lead to unexpected results since it can invoke multiple conversion
methods, including static_cast, dynamic_cast, const_cast, and
reinterpret_cast, depending on the context.
In addition, the compiler may not optimize C-style casts as effectively
since they lack explicit intent, making it harder for the compiler to
understand what type conversions are valid.
Syntax
( TargetType )initializer;
C++-style Casting (Recommend for Safety)
Explanation
C++ introduced several specific casting operators to provide more
clarity and safety in type conversion. These are static_cast,
dynamic_cast, const_cast, and reinterpret_cast.
These four casts do not introduce any new functionality to C++. All of
them can be implemented using C-style casting.
They merely clarify the intent behind the cast.
static_cast (Recommend for Safety and Performance)
Explanation
static_cast is used for safe, compile-time type conversion.
It can be used for well-defined conversions between related types, such
as upcasting and downcasting in inheritance hierarchies, without
runtime checks.
It is safer than C-style casting but still requires caution,
especially during downcasting.
This cast involves compile-time checks and is generally efficient, as
it can be optimized by the compiler.
Syntax
static_cast< TargetType >( initializer );
Usage
Primitive type conversion.
Pointer type conversion within inheritance.
Converting void pointer to another pointer type.
All implicit conversions.
Limitations
Cannot cast between incompatible types:
It only works with related types. For example, you cannot use static_cast
to convert an unrelated class to another.
No runtime type safety:
If static_cast is used to downcast in an inheritance hierarchy, it
won't check the actual type at runtime.
This can lead to undefined behavior if the object isn't of the expected
type.
Not for casting between pointers and non-pointers:
You cannot cast between objects and pointers (e.g., from an integer to
a pointer) with static_cast. For such conversions, reinterpret_cast is
required.
No removal of const or volatile qualifiers:
static_castcannot add or remove const or volatile qualifiers.
Use const_cast for this purpose.
Requirements for Using static_cast
Types must be compatible:
The conversion must make sense in the context of the C++ type system (e.g.,
converting between base and derived classes, or between numeric types).
Inheritance hierarchy:
You can only cast pointers or references within the same class hierarchy
(i.e., between base and derived classes).
Known at compile time:
The conversion must be determined at compile time, as static_cast does
not involve any runtime checks.
dynamic_cast (Recommend for Safety but Not Recommend for Performance)
Explanation
dynamic_cast is designed for safe, runtime-checked downcasting in
polymorphic hierarchies.
It ensures that the cast is valid at runtime, making it the safest
option for casting within class hierarchies.
If the cast is unsuccessful, it returns nullptr (for pointers) or
throws an exception (for references, std::bad_cast), thus enhancing
type safety.
However, this added safety can result in slower performance compared to
static_cast.
The runtime check in dynamic_cast is implementedusing the
Run-Time Type Information (RTTI) mechanism in C++, which includes type
information and the virtual table (vtable).
Its downcasting workflow involves RTTI lookup, hierarchy
traversal, and pointer adjustment.
dynamic_cast requires at least one virtual function.
Because it slows down performance, we can use a macro to determine whether
to use it in debug mode or release mode.
Last, we should check if RTTI is enabled in our compiler. (GCC/G++
enables it by default, while MSVC does not.)
classBase {
public:virtual Type funcName(){ ... };
virtual~Base() = default;
};
classDerived: publicBase { ... };
intmain() {
Base base; // Not a Derived instance.try {
// This will throw `std::bad_cast` because base is not of type Derived.
Derived& der_ref = dynamic_cast< Derived& >( base );
} catch( const std::bad_cast& e ) {
std::cout << "Caught exception: " << e.what() << '\n';
};
};
Limitations
Polymorphic base class required:
dynamic_castonly works if the base class has at least one virtual
function (typically a virtual destructor).
Slight performance overhead:
Since dynamic_cast performs runtime type checking, it is slower than
static_cast, especially in complex class hierarchies.
Works only on pointers and references:
You cannot use dynamic_cast on regular objects. It only works on pointers
or references.
Downcast safety depends on runtime type:
If the object isn't of the expected type, the cast will either return
nullptr (for pointers) or throw a std::bad_cast exception (for
references).
Requires RTTI (Run-Time Type Information):
RTTI must be enabled in the compiler (it’s usually enabled by default). If
RTTI is disabled, dynamic_cast will not work.
Requirements for Using dynamic_cast
Polymorphic base class:
The base class involved in the cast must contain at least one virtual
function, typically a virtual destructor.
Pointer or reference types:
dynamic_cast can only be used to cast pointers or references—it
won't work for normal values.
Valid object type at runtime:
For downcasting, the object at runtime must match the derived type you're
casting to; otherwise, the cast fails.
const_cast (Recommend for Safety but Not Recommend for Performance)
Explanation
const_cast is used for adding or removing const or volatile
qualifiers from a pointer or reference.
It should be used sparingly and only when necessary, as misuse
can easily lead to undefined behavior.
This cast involves compile-time checks and typically incurs minimal
performance overhead since it only modifies type qualifiers without
altering the underlying object.
const_castonly adds or removes the const or volatile qualifiers;
it doesn’t convert between unrelated types.
Syntax
const_cast< TargetType >( initializer );
Usage
Removing const to modify a non-const object.
Adding or removing volatile.
reinterpret_cast (Recommend for Performance but Not Recommend for Safety)
Explanation
reinterpret_cast is intended for low-level reinterpretation of an
object’s bit pattern, which allows conversion between incompatible types.
It is the most powerful and dangerous cast, used in situations where
other casts are insufficient.
This cast does not check type compatibility and can lead to undefined
behavior if misused, so it should be used with caution.
While it can achieve results similar to type punning, it comes with
significant caveats regarding safety and portability.
Its performance is generally comparable to C-style casting, as it
does not perform any type checks and simply reinterprets the bits.
Bit patterns refer to interpreting data as binary values and
considering how many bytes it occupies.
Syntax
reinterpret_cast< TargetType >( initializer );
Usage
Pointer type conversions:
It is commonly used to convert one pointer type to another, even between
unrelated types.
Reference type conversions:
Similar to pointers, reinterpret_cast can convert one reference type to
another.
This allows for casting references of different types, but care must be
taken to ensure the underlying object types are compatible.
Casting between pointer and integer types:
It can be used to cast pointers to integral types (e.g., uintptr_t) and
vice versa.
The integral types must be either a 32-bit unsigned or a 64-bitunsigned type, depending on the system architecture.
Interfacing with hardware or system-level code:
Useful in systems programming or when dealing with low-level constructs,
such as when interfacing with hardware or legacy C libraries.
Requirements for Using reinterpret_cast
Pointer or reference Types:
The expression being cast must be a pointer or reference type.
Correctness of the cast:
Ensure that the object being accessed through the cast pointer is
compatible with the target type.
Avoid using reinterpret_cast on pointers of unrelated types
unless you are sure of what you are doing.
Notes
We recommend adopting C++-style casting for new projects, smaller
codebases, or if you are new to C++, as it offers a safer alternative.
It is essential to understandwhether a particular cast involves
runtime or compile-time checks.
The distinct naming of these casts enhances code readability and
facilitates easy identification during code searches. (Search for their
names to identify where explicit conversions occur.)
When converting a void pointer type to a specific pointer type (or vice
versa), static_cast is the preferred choice. It is best used for
casting between void pointer type and other pointer types, provided there’s
no need for low-level reinterpretation.
const lvalue references can bind to temporary results directly.
Non-const lvalue references cannot bind to temporaries directly; you need
a separate variable.
Rvalue references can bind to temporaries, including results of casts and
conversions.
explicit
Explanation
The explicit keyword in C++ is used to prevent implicit conversions
when defining constructors or conversion operators.
By marking a constructor as explicit, you ensure that it can only be called
with a direct initialization, thus avoiding unintended conversions that
might lead to errors.
This feature enhances type safety and code readability by making the
programmer's intentions clear.
std::bit_cast (Recommended for Safety, Performance and Type Integrity)
Explanation
std::bit_cast is designed for safe reinterpretation of an object’s
bit pattern, allowing conversion between types of the same size.
It provides a type-safe mechanism for casting, ensuring that the sizes of
the source and target types are equalat compile time.
This function is less risky than reinterpret_cast, as it prevents
undefined behavior that can arise from size mismatches or type
incompatibility.
std::bit_cast is ideal for type punning while maintaining safety and
portability, making it a preferable choice for most use cases.
The performance is comparable to reinterpret_cast, as it also does
not incur overhead from type checking, but it provides stronger
guarantees about correctness.
Bit patterns refer to interpreting data as binary values and
considering how many bytes it occupies, with std::bit_cast ensuring the
integrity of those bits.
Syntax
// Declaration syntax.template< typename To, typename From >
constexpr To std::bit_cast( const From& src );
Commonly used to reinterpret bit patterns between types like integers and
floating-point numbers.
Casting structs or classes:
Can be used to cast complex types like structs or classes to and
frombyte arrays or other types, ensuring the bit representation is
preserved.
Safe type punning:
Useful in scenarios where you need to access the underlying bit
representation of an object without risking undefined behavior.
Requirements for Using std::bit_cast
Sametsize types:
The source and target types must be of the same size;
otherwise, a compile-time error will occur.
Type compatibility:
While std::bit_cast does not require types to be related, it is
crucial to ensure the types make sense for the intended interpretation of
the data.
Constexpr support:
It can be used in constant expressions, making it suitable for
scenarios that require compile-time evaluations.
Differences between std::bit_cast and reinterpret_cast
Safety and Type Checking
std::bit_cast:
Type safety:
std::bit_cast ensures that the source and destination types are the
same sizeat compile time.
If they are not, it will result in a compilation error.
Undefined behavior:
It avoids undefined behavior by enforcing size constraints and ensuring
that both types can be safely represented with the same number of bits.
reinterpret_cast:
Type safety:
reinterpret_cast does not perform any compile-time checks on size or
type compatibility.
It allows for casting between unrelated types, which can lead to
undefined behavior if misused.
Undefined behavior:
If you attempt to access the bits of the reinterpreted type and the
sizes are not compatible, it can result in undefined behavior.
Intent and Purpose
std::bit_cast:
Intent:
The primary intent of std::bit_cast is to safely reinterpret the bit
representation of an object without modifying its underlying data.
It is used when you want to change the type of the object while
preserving its bit pattern.
Use cases:
It is suitable for scenarios like converting between integer types and
floating-point types or safely casting structs to byte arrays.
reinterpret_cast:
Intent:
reinterpret_cast is used for low-level, potentially unsafe type
conversions.
It conveys a need for flexibility in converting between pointer types,
reference types, or even between pointers and integers.
Use cases:
Commonly used in systems programming, interfacing with hardware, or
when interacting with legacy C libraries where type safety is not a
priority.
Performance
std::bit_cast:
Performance:
It has performance characteristics similar to reinterpret_cast, as it
does not incur the overhead of type checks at runtime.
However, it provides stronger guarantees regarding type safety.
reinterpret_cast:
Performance:
Its performance is generally comparable to C-style casting, as it
performs no safety checks and directly reinterprets the bits.
Requirements for Usage
std::bit_cast:
Same size:
The source and target types must be of the same size.
constexpr support:
Can be used in constant expressions.
reinterpret_cast:
Pointer or reference types:
The expression being cast must be a pointer or reference type.
Correctness of the cast:
Users must ensure that the object being accessed through the cast
pointer is compatible with the target type.
Upcasting and Downcasting
Explanation
Upcasting and downcasting are terms used to describe type conversions
within an inheritance hierarchy (i.e., between a base class and its derived
classes).
These casts typically involve pointer or reference conversions.
Upcasting
Upcasting refers to casting a derived class object to a base class type.
This conversion is safe and performed implicitly or explicitly
using static_cast.
Since the derived class contains all the members of the base class, the cast
will always succeed.
Base class members will be accessible, but derived class-specific
members will be hiddenunless the base class method is marked as
virtual (allowing for polymorphism).
Downcasting
Downcasting refers to casting a base class object to a derived class
type.
This is potentially unsafe, as the base class might not actually be an
instance of the derived class.
Therefore, dynamic_cast should be used when performing downcasting to
ensure runtime type safety.
dynamic_cast should be used for downcasting when you are unsure if
the base pointer refers to an instance of the derived class.
Notes
Learning this topic through practice rather than theory.