c++type-punningbit-cast

How to use std::bit_cast on an array pointer type argument


Is it possible to pass address of the first element of an array as argument to std::bit_cast.

#include <bit>
#include <iostream>

struct A {
    int a;
    int b;
    int c;
    int d;
    int e;

    void print() {
        std::cout << a << ", " << b << ", " << c << ", " << d << ", " << e << std::endl;
    }
};

int main() {
    char arr[20]{};
    // arr is filled with some data
    A a = std::bit_cast<A>(arr);
    a.print();

    const char* arrp = arr;
    // doesn't compile
    A b = std::bit_cast<A>(arrp);
    b.print();
}

Error

<source>: In function 'int main()':
<source>:23:27: error: no matching function for call to 'bit_cast<A>(const char*&)'
   23 |     A b = std::bit_cast<A>(arrp);
      |           ~~~~~~~~~~~~~~~~^~~~~~
In file included from <source>:1:
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bit:81:5: note: candidate: 'template<class _To, class _From> constexpr _To std::bit_cast(const _From&) requires  sizeof (_To) == sizeof (_From) && __is_trivially_copyable(_To) && __is_trivially_copyable(_From)'
   81 |     bit_cast(const _From& __from) noexcept
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bit:81:5: note:   template argument deduction/substitution failed:
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bit:81:5: note: constraints not satisfied
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bit: In substitution of 'template<class _To, class _From> constexpr _To std::bit_cast(const _From&) requires  sizeof (_To) == sizeof (_From) && __is_trivially_copyable(_To) && __is_trivially_copyable(_From) [with _To = A; _From = const char*]':
<source>:23:27:   required from here
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bit:81:5:   required by the constraints of 'template<class _To, class _From> constexpr _To std::bit_cast(const _From&) requires  sizeof (_To) == sizeof (_From) && __is_trivially_copyable(_To) && __is_trivially_copyable(_From)'
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bit:83:27: note: the expression 'sizeof (_To) == sizeof (_From) [with _To = A; _From = const char*]' evaluated to 'false'
   83 |     requires (sizeof(_To) == sizeof(_From))
      |              ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
Compiler returned: 1

Solution

  • Arrays are not pointers. std::bit_cast<A>(arr); is correct. arr is passed by reference, and because std::bit_cast has a const From& parameter, From deduces to char[20] and you're effectively passing const char(&)[20], i.e. "reference to array of 20 const char". This is not the same as passing a pointer.

    On the other hand, std::bit_cast<A>(arrp); attempts to bit-cast a pointer (with size 8, presumably) to an A (with size 20, presumably), and you cannot bit-cast between differently sized types.

    The better approach here is to do type punning through std::memcpy, which works because A is trivially copyable and implicit-lifetime:

    const char* arrp = arr;
    
    A b;
    std::memcpy(&b, arrp, sizeof(b));
    b.print();
    

    See also What is the modern, correct way to do type punning in C++?