std::bit_cast(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’… std::bit_cast(3)

okay so this is the C++ Standard Libary manual page β€” yes, the header really says β€œLibary,” and we are not editing the facts, bestie πŸ’…. this is the std::bit_cast(3) glow-up, and the vibe check is: all the bits, all the caveats, all the slay ✨

🌸 NAME 🌸

std::bit_cast β€” std::bit_cast. literally the name, iconic, no notes πŸ’–

✨ Synopsis ✨

Defined in header <bit>.

Since: C++20

template< class To, class From >
constexpr To bit_cast( const From& from ) noexcept;

okay so what does it do? It obtains a value of type To by reinterpreting the object representation of From. Every bit in the value representation of the returned To object is equal to the corresponding bit in the object representation of from. The values of padding bits in the returned To object are unspecified. periodt.

If there is no value of type To corresponding to the value representation produced, the behavior is undefined. If there are multiple such values, which value is produced is unspecified. so like, the universe may choose, and that’s legal.

A bit in the value representation of the result is indeterminate if it:

  • does not correspond to a bit in the value representation of From (i.e. it corresponds to a padding bit), or
  • corresponds to a bit of an object that (until C++26) for which the smallest enclosing object (since C++26) is not within its lifetime, or
  • has an indeterminate value.

A bit in the value representation of the result is erroneous if it corresponds to a bit for which the smallest enclosing object has an (since C++26) erroneous value.

The result does not otherwise contain any indeterminate or erroneous values.

For each bit in the value representation of the result that is indeterminate, the smallest object containing that bit has an indeterminate value; the behavior is undefined unless that object is (until C++26) of an uninitialized-friendly type.

The result does not otherwise contain any indeterminate values.

For each bit b in the value representation of the result that is indeterminate or erroneous, let u be the smallest object enclosing b:

  • If u is of uninitialized-friendly type, u has an indeterminate value if any of the bits in its value representation are (since C++26) indeterminate, or otherwise has an erroneous value.
  • Otherwise, if b is indeterminate, the behavior is undefined.
  • Otherwise, the behavior is erroneous, and the result is as specified above.

This overload participates in overload resolution only if sizeof(To) == sizeof(From) and both To and From are TriviallyCopyable types.

This function template is constexpr if and only if each of To, From and the types of all subobjects of To and From:

  • is not a union type;
  • is not a pointer type;
  • is not a pointer to member type;
  • is not a volatile-qualified type; and
  • has no non-static data member of reference type.

πŸŽ€ Parameters πŸŽ€

  • from β€” the source of bits for the return value. it’s giving β€œhand over the bits, bestie” πŸ’…

πŸ¦‹ Return value πŸ¦‹

An object of type To whose value representation is as described above. slay.

🧚 Possible implementation 🧚

To implement std::bit_cast, ignoring the fact that it’s constexpr, std::memcpy can be used, when it is needed, to interpret the object representation as one of another type:

template<class To, class From>
std::enable_if_t<
    sizeof(To) == sizeof(From) &&
    std::is_trivially_copyable_v<From> &&
    std::is_trivially_copyable_v<To>,
    To>
// constexpr support needs compiler magic
bit_cast(const From& src) noexcept
{
    static_assert(std::is_trivially_constructible_v<To>,
        "This implementation additionally requires "
        "destination type to be trivially constructible");

    To dst;
    std::memcpy(&dst, &src, sizeof(To));
    return dst;
}

πŸ’œ Notes πŸ’œ

reinterpret_cast (or equivalent explicit cast) between pointer or reference types shall not be used to reinterpret object representation in most cases because of the type aliasing rule. bestie, do not do it, the compiler will not be cute about it 😭

Feature-test macro: __cpp_lib_bit_cast β€” Value: 201806L β€” Std: (C++20) β€” Feature: std::bit_cast.

🌈 Example 🌈

// Run this code

#include <bit>
#include <cstdint>
#include <iostream>

constexpr double f64v = 19880124.0;
constexpr auto u64v = std::bit_cast<std::uint64_t>(f64v);
static_assert(std::bit_cast<double>(u64v) == f64v); // round-trip

constexpr std::uint64_t u64v2 = 0x3fe9000000000000ull;
constexpr auto f64v2 = std::bit_cast<double>(u64v2);
static_assert(std::bit_cast<std::uint64_t>(f64v2) == u64v2); // round-trip

int main()
{
    std::cout
        << "std::bit_cast<std::uint64_t>(" << std::fixed << f64v << ") == 0x"
        << std::hex << u64v << '\n'
        << "std::bit_cast<double>(0x" << std::hex << u64v2 << ") == "
        << std::fixed << f64v2 << '\n';
}

⭐ Possible output ⭐

std::bit_cast<std::uint64_t>(19880124.000000) == 0x4172f58bc0000000
std::bit_cast<double>(0x3fe9000000000000) == 0.781250

πŸ”₯ Defect reports πŸ”₯

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

  • DR: CWG 2482 β€” Applied to: C++20 β€” Behavior as published: it was unspecified whether UB would occur when involving indeterminate bits β€” Correct behavior: specified (P1272R4)

🫢 See also 🫢

  • start_lifetime_as
  • start_lifetime_as_array (C++23)

both of these: implicitly creates objects in given storage with the object representation reused (function template).

source: http://cppreference.com Β· version/date: 2024.06.10 Β· page: std::bit_cast(3) β€” mwah πŸ’‹

πŸ“¦ where this page comes from