Post

[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 const or volatile.

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:

Resulttarget-type
lvalue1. lvalue reference T&
2. rvalue reference to a function type T&&
xvaluervalue reference to an object type T&&
prvalueotherwise (non-reference T, or voidprvalue(void))

Glossary
Ill-formed: rejected at compile time.
UB: undefined behavior at runtime.
Unspecified: implementation chooses a valid outcome.

Notes

Allowed conversions

  1. Downcast in non-virtual inheritance (B → D)
    • B is an unambiguous, accessible non-virtual base of complete class D, then static_cast<D&>(b) or static_cast<D*>(pB) is allowed.
    • No runtime check is performed; using it when the dynamic object is not a D is UB(Undefined Behavior).
    • Casting from a member subobject that is not a base subobject is UB.
    • Prefer dynamic_cast if 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
     }
    
  2. Cast to rvalue reference (move)
    • If target-type is T&& 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.)
  3. Direct-initialization style conversion
    • If there’s an implicit conversion sequence to target-type, or if target-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.
  4. Conversion to void
    • static_cast<void>(expr) evaluates expr and discards the value.
  5. Inverse of certain standard conversions
    • If there exists a standard conversion from target-type to 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.
  6. Make certain standard conversions explicit
    • You may explicitly perform lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversions.
  7. 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.
  8. 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.
  9. 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.
  10. Pointer-to-member upcast
    • D::* may be upcast to B::* if B is an unambiguous, accessible base of complete D.
    • No runtime check is made; using the resulting member pointer where the member doesn’t exist is UB.
  11. void*T*
    • A void* prvalue may be converted to T*.
    • If the resulting pointer fails T’s alignment requirement, the value is unspecified.
    • When the original pointer points to an object a and there exists a pointer-interconvertible object b of a type similar to T, the result points to b; 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”.)

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 (use const_cast).
  • It is not for arbitrary bit-level reinterpretation between unrelated types (that’s reinterpret_cast and 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-rangeUB.
  • static_cast does not remove const/volatile (use const_cast).

Since / Changed in

  • C++17: For non-reference targets, static_cast to 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

  1. C++ documentation
This post is licensed under CC BY 4.0 by the author.