Question
I am confused about when to use reinterpret_cast versus static_cast in C++.
From what I understand, static_cast is generally used when the conversion is well-defined and can be checked at compile time. It is also the kind of cast the compiler commonly performs for many implicit conversions.
reinterpret_cast seems to be used mainly in cases like these:
- converting integer types to pointer types, and vice versa
- converting one pointer type to another pointer type
My understanding is that this kind of cast is often low-level, potentially non-portable, and should usually be avoided.
The specific case I am unsure about is C and C++ interoperability. I am calling C++ code from C, and the C code needs to store a C++ object pointer as a void*. Which cast should be used to convert between void* and a class pointer type?
For example:
class MyClass {
public:
void hello();
};
void* store_object(MyClass* obj) {
// Which cast is correct here?
return ???;
}
MyClass* load_object(void* ptr) {
// Which cast is correct here?
return ???;
}
I have seen both static_cast and reinterpret_cast used for this. It seems like static_cast may be preferable if the conversion is well-defined, but I also read that reinterpret_cast is used for pointer-to-pointer conversions. Which one is correct in this situation, and why?
Short Answer
By the end of this page, you will understand the difference between static_cast and reinterpret_cast in C++, especially for pointer conversions. You will also learn the correct cast to use when converting between void* and a class pointer in C/C++ interoperability code, and why reinterpret_cast is usually reserved for lower-level, riskier conversions.
Concept
In C++, different cast operators communicate different intentions and levels of safety.
static_cast is for well-defined conversions that the language understands. These include things like:
- numeric conversions such as
inttodouble - converting a derived class pointer to a base class pointer
- converting
void*back to the original object pointer type - calling explicit constructors or conversion operators
reinterpret_cast is for low-level bit-pattern reinterpretation. It tells the compiler: “treat these bits as if they were another type.” It does not usually perform a meaningful semantic conversion. Instead, it reinterprets the value at a lower level.
That is why reinterpret_cast is commonly associated with:
- pointer type punning
- converting pointers to integers and back
- interacting with low-level APIs, memory-mapped hardware, or serialization hacks
For your specific case, converting between void* and MyClass* is a standard, well-defined pointer conversion in C++. That means static_cast is the correct choice.
Example:
void* p = static_cast<*>(obj);
MyClass* obj2 = <MyClass*>(p);
Mental Model
Think of C++ casts like different kinds of permission slips.
static_castis like changing a label in a way the system understands. For example, moving a package from a box labeled “generic object” (void*) back into a box labeled “MyClass*`. The object is still the same object.reinterpret_castis like telling someone to treat a package of screws as if it were a package of batteries just because the box is the same size. Sometimes low-level code does this on purpose, but it is risky and easy to misuse.
For void* and class pointers, you are not changing what the object is. You are just temporarily storing it in a generic container. That is exactly the kind of job static_cast is meant for.
Syntax and Examples
The key syntax is:
static_cast<T>(expression)
reinterpret_cast<T>(expression)
Correct use for void* and class pointers
class MyClass {
public:
void hello() {}
};
void* store_object(MyClass* obj) {
return static_cast<void*>(obj);
}
MyClass* load_object(void* ptr) {
return static_cast<MyClass*>(ptr);
}
Why this is correct
MyClass*tovoid*is a standard pointer conversion.void*back toMyClass*is also supported when thevoid*originally came from that object pointer.static_castexpresses that this is a normal, language-supported conversion.
Step by Step Execution
Consider this example:
class MyClass {
public:
void hello() {}
};
int main() {
MyClass obj;
void* raw = static_cast<void*>(&obj);
MyClass* typed = static_cast<MyClass*>(raw);
typed->hello();
}
Step by step:
-
MyClass obj;- A
MyClassobject is created on the stack.
- A
-
void* raw = static_cast<void*>(&obj);&objis aMyClass*.- It is converted to
void*. - The address does not change; only the type used to describe it becomes generic.
-
MyClass* typed = static_cast<MyClass*>(raw);- The generic pointer is converted back to
MyClass*.
- The generic pointer is converted back to
Real World Use Cases
This concept appears often in practical C++ programming.
1. C and C++ interoperability
C APIs cannot directly store C++ class types, so they often use opaque handles:
extern "C" void* widget_create();
extern "C" void widget_destroy(void* handle);
Internally, the handle is really a C++ object pointer.
2. Generic callback user data
Many C libraries let you pass a void* context pointer:
void register_callback(void (*fn)(void*), void* user_data);
C++ code stores an object pointer in user_data and later converts it back.
3. Plugin systems and native bindings
Frameworks that bridge languages or modules often pass around untyped handles and recover typed pointers internally.
4. Resource wrappers
A graphics, audio, or database library may expose opaque handles in public C APIs while managing rich C++ objects under the hood.
Real Codebase Usage
In real projects, developers usually avoid exposing C++ classes directly across a C boundary. Instead, they use an opaque handle pattern.
Common pattern: opaque handle API
class Connection {
public:
bool open() { return true; }
};
extern "C" void* connection_create() {
return static_cast<void*>(new Connection());
}
extern "C" int connection_open(void* handle) {
if (!handle) {
return 0;
}
Connection* conn = static_cast<Connection*>(handle);
return conn->open() ? 1 : 0;
}
extern "C" void connection_destroy(void* handle) {
if (!handle) {
;
}
Connection* conn = <Connection*>(handle);
conn;
}
Common Mistakes
1. Using reinterpret_cast when static_cast is enough
Broken style:
MyClass* obj = reinterpret_cast<MyClass*>(ptr);
Better:
MyClass* obj = static_cast<MyClass*>(ptr);
Why: void* to object pointer is a standard conversion, so static_cast is clearer and safer in intent.
2. Casting a void* to the wrong type
int x = 10;
void* ptr = &x;
MyClass* obj = static_cast<MyClass*>(ptr); // compiles
obj->hello(); // undefined behavior
Avoid this by making sure the void* really came from the same original type.
3. Assuming reinterpret_cast performs value conversion
Broken assumption:
x = ;
* f = <*>(&x);
Comparisons
| Cast | Typical use | Checked by type system? | Safe for void* ↔ object pointer? | Notes |
|---|---|---|---|---|
static_cast | Well-defined conversions | Yes, within language rules | Yes | Best choice for void* and object pointer conversions |
reinterpret_cast | Low-level reinterpretation | Very limited | Can compile, but not preferred here | Use only when you need bit-level or low-level pointer reinterpretation |
dynamic_cast | Safe polymorphic downcasting | Yes, at runtime | No | Used with inheritance and virtual functions |
| C-style cast |
Cheat Sheet
// Generic syntax
static_cast<T>(expr)
reinterpret_cast<T>(expr)
Use static_cast for
- numeric conversions
void*to original object pointer type- object pointer to
void* - base/derived conversions that are statically valid
- explicit constructor or conversion operations
void* p = static_cast<void*>(obj);
MyClass* obj2 = static_cast<MyClass*>(p);
Use reinterpret_cast for
- low-level pointer reinterpretation
- pointer-to-integer or integer-to-pointer conversions
- rare systems programming cases
std::uintptr_t raw = reinterpret_cast<std::uintptr_t>(ptr);
Rules of thumb
- Prefer the least powerful cast that works.
- If
static_castcan do it, usestatic_cast. - does not mean “convert safely.” It means “reinterpret at a low level.”
FAQ
Should I use static_cast or reinterpret_cast for void* to class pointer in C++?
Use static_cast. Converting between void* and an object pointer type is a standard, well-defined conversion.
Why does reinterpret_cast also seem to work for void* conversions?
Because it is very permissive for pointer conversions. But that does not make it the best tool. static_cast better expresses the actual language-supported conversion.
Is converting a class pointer to void* safe?
Yes. Converting an object pointer to void* and back to the same original type is a normal and common pattern.
Can static_cast from void* detect the wrong type at runtime?
No. It only performs the conversion. You must ensure that the void* really points to the expected object type.
When should I really use reinterpret_cast in C++?
Use it for low-level operations where you intentionally need to reinterpret bits or raw addresses, such as some systems programming tasks.
Mini Project
Description
Build a tiny C-compatible wrapper around a C++ class using an opaque void* handle. This demonstrates the correct use of static_cast when storing and recovering a C++ object pointer across a C-style API boundary.
Goal
Create, use, and destroy a C++ object through a C-style API that exposes only void* handles.
Requirements
- Define a simple C++ class with at least one method.
- Expose
create,use, anddestroyfunctions withextern "C". - Store the object as a
void*in the public API. - Convert between
void*and the class pointer using the correct cast. - Handle null pointers safely in the API functions.
Keep learning
Related questions
Advantages of Brace Initialization in C++
Learn why C++ brace initialization is often clearer and safer than other object initialization styles, with examples and common pitfalls.
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Aggregates, Trivial Types, Trivially Copyable Types, and PODs Explained
Learn what aggregates, trivial types, trivially copyable types, and PODs mean in C++, how they differ, and why they matter.