c++c++20type-punningbit-cast

What, if any, additional UB will bit_cast have beyond memcpy?


I have a class whose purpose is to move data which might have alignment restrictions to or from a serialized memory buffer where the data is not aligned. I have set and get handlers as follows:

#include <cstring>
template<typename T>
struct handle_type {    
    static inline T get(const void *data) {
        T value;
        memcpy(&value, data, sizeof(T));
        return value;
    }
    static inline void set(void *data, T value) {
        memcpy(data, &value, sizeof(T));
    }
};

After c++20 comes out, I am hoping it will become something like the following:

#include <bit>
template<typename T>
struct handle_type {    
    union unaligned { char bytes[sizeof(T)];  }; // EDIT: changed from struct
    static inline T get(const void *data) {
        return std::bit_cast<T>(*(const unaligned*)data);
    }
    static inline void set(void *data, T value) {
        *(unaligned *)data = std::bit_cast<unaligned>(value);
    }
};

Will it work? Or is the fact that I am using an inerim type, the unaligned struct type, liable to pose a problem?


Solution

  • Your get function is UB unless the user has provided a pointer to an unaligned object. Your set function is similarly UB unless data is an unaligned object. Either of these cases violates strict aliasing. Remember: the strict aliasing back-door is about an actual char*; that's not the same as an object that happens to contain a char*.

    Also, set is potentially a compile error, depending entirely on whether or not the implementation of unaligned is such that it has the same size as T. After all, unaligned may have padding at the end.

    bit_cast wants to deal primarily in objects, not random buffers of memory. For that, you should stick with memcpy.


    <changed to use union instead of struct>

    That changes nothing; using a union doesn't guarantee that the size of the union is equal to the size of its largest data member. From [class.union]/2:

    The size of a union is sufficient to contain the largest of its non-static data members.

    Emphasis added: "sufficient", not "equal to". The standard permits implementations the ability to make the union larger than its largest data member, as such a size would still be "sufficient".