[C++] Conversion: static_cast
[C++] Conversion: static_cast
Definition
1
static_cast<target-type>(expression)
- Converts between types using a combination of implicit and user-defined conversions.
- The conversion is determined at compile time and does not cast away
constorvolatile.
Type Argument
target-type is the destination type (any type-id this conversion permits):
- fundamental/enum/class
- pointer, pointer-to-member
- function type,
void - reference (
T&,T&&).
Parameters
expression is the source expression (value, reference, pointer, or pointer-to-member) to be converted.
Return Value
The result has type target-type. Its value category depends on target-type:
| Result | target-type |
|---|---|
| lvalue | 1. lvalue reference T& 2. rvalue reference to a function type T&& |
| xvalue | rvalue reference to an object type T&& |
| prvalue | otherwise (non-reference T, or void → prvalue(void)) |
Glossary
Ill-formed: rejected at compile time.
UB: undefined behavior at runtime.
Unspecified: implementation chooses a valid outcome.
Notes
Allowed conversions
- Downcast in non-virtual inheritance (B → D)
- B is an unambiguous, accessible non-virtual base of complete class
D, thenstatic_cast<D&>(b)orstatic_cast<D*>(pB)is allowed. - No runtime check is performed; using it when the dynamic object is not a
Dis UB(Undefined Behavior). - Casting from a member subobject that is not a base subobject is UB.
- Prefer
dynamic_castif you need a checked downcast.
1 2 3 4 5 6 7 8 9 10 11
struct B {}; struct D : B { B b; }; int main() { D d; B& br1 = d; B& br2 = d.b; static_cast<D&>(br1); // OK: lvalue denoting the original d object static_cast<D&>(br2); // UB: the b subobject is not a base class subobject }
- B is an unambiguous, accessible non-virtual base of complete class
- Cast to rvalue reference (move)
- If
target-typeisT&&and is reference-compatible with the operand,static_cast<T&&>(expression)turns the expression into an xvalue (move). - Access-control/ambiguity rules for bases apply; bit-field lvalues are first converted to a prvalue of the underlying type. (Used by
std::move.)
- If
- Direct-initialization style conversion
- If there’s an implicit conversion sequence to
target-type, or iftarget-type temp(expression)would be a viable direct-initialization (constructor or conversion operator), the cast performs that. - Since C++17, for non-reference
target-type, the prvalue result is the directly-initialized result object itself.
- If there’s an implicit conversion sequence to
- Conversion to
voidstatic_cast<void>(expr)evaluatesexprand discards the value.
- Inverse of certain standard conversions
- If there exists a standard conversion from
target-typeto the source type(expr) excluding lvalue-to-rvalue, array-to-pointer, function-to-pointer, null-pointer, null-member-pointer, (since C++17) function-pointer, or boolean conversions, the cast may perform the inverse.
- If there exists a standard conversion from
- Make certain standard conversions explicit
- You may explicitly perform lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversions.
- Scoped enum → integer / floating-point
- A scoped enumeration value can be converted to an integer or floating-point type.
- For
bool, zero → false, non-zero → true. - For other integral types, if the enum value doesn’t fit, the result was unspecified (until C++20); since C++20, the result matches converting from the enum’s underlying type.
- Integer / enum / floating-point → (complete) enum
- No fixed underlying type: casting an out-of-range value yields UB.
- Fixed underlying type: convert to the underlying type first, then to the enum.
- Floating-point → enum: likewise, via the underlying type.
- Floating-point → floating-point (explicit) (since C++23)
- If exactly representable, the value is unchanged.
- If it lies between two representable values, the result is implementation-defined (typically “round to nearest” under IEEE arithmetic). -Otherwise, UB.
- Pointer-to-member upcast
D::*may be upcast toB::*ifBis an unambiguous, accessible base of completeD.- No runtime check is made; using the resulting member pointer where the member doesn’t exist is UB.
void*↔T*- A
void*prvalue may be converted toT*. - If the resulting pointer fails
T’s alignment requirement, the value is unspecified. - When the original pointer points to an object
aand there exists a pointer-interconvertible objectbof a type similar toT, the result points tob; otherwise the pointer value is unchanged. - Conversion to
void*and back to the original (or more cv-qualified) type preserves the value. (See C++20 traits for “pointer-interconvertible”.)
- A
Overload disambiguation
You can disambiguate function overloads by casting to a specific function-pointer type and passing that to algorithms (e.g., std::for_each)
1
2
std::for_each(files.begin(), files.end(),
static_cast<std::ostream&(*)(std::ostream&)>(std::flush));
What static_cast cannot do
- It cannot remove
const/volatile(useconst_cast). - It is not for arbitrary bit-level reinterpretation between unrelated types (that’s
reinterpret_castand is inherently unsafe/low-level)
Example
Click Run ▶
Pitfalls
- Casting a subobject that is not a base subobject to the derived type → UB.
- Virtual base (or base of a virtual base), ambiguous, or inaccessible base in downcasts → ill-formed.
void*→T*with misaligned address → unspecified value.- Enum conversions: no fixed underlying type and out-of-range → UB.
static_castdoes not removeconst/volatile(useconst_cast).
Since / Changed in
- C++17: For non-reference targets,
static_castto a class type yields the result object prvalue (temporary materializes only when required). - C++20: Scoped enum → integral conversions defined via underlying type (no “unspecified fit”).
- C++23: Explicit floating-point → floating-point conversion semantics clarified (out-of-range → UB).
See also
const_cast,dynamic_cast,reinterpret_cast- Implicit conversions (lvalue-to-rvalue / array-to-pointer / function-to-pointer)
- Value categories (lvalue / xvalue / prvalue)
References
This post is licensed under CC BY 4.0 by the author.