Каков метод no- undefined -behavior для десериализации объекта из массива байтов в С++ 11 (или более поздней версии)?

Чтобы устранить проблемы с выравниванием, мне нужно memcpy во временное. Какой тип должен быть временным? gcc жалуется, что следующий reinterpret_cast нарушит строгие правила псевдонимов:

template <typename T>
T deserialize(char *ptr) {
    static_assert(std::is_trivially_copyable<T>::value, "must be trivially copyable");
    alignas(T) char raw[sizeof(T)];
    memcpy(raw, ptr, sizeof(T));
    return *reinterpret_cast<T *>(raw);
}

(например, когда T является "длинным" ).

Я не хочу определять T, так как я не хочу создавать T перед его перезаписью.

В объединении не записывается один элемент, а затем считывается другой счет как поведение undefined?

template<typename T>
T deserialize(char *ptr) {
    union {
        char arr[sizeof(T)];
        T obj;
    } u;

    memcpy(u.arr, ptr, sizeof(T));   // Write to u.arr
    return u.obj;   // Read from u.obj, even though arr is the active member.
}

Ответ 1

Что вы хотите, так это:

T result;
char * p = reinterpret_cast<char *>(&result);   // or std::addressof(result) !

std::memcpy(p, ptr, sizeof(T));                 // or std::copy!!

return result;

Нет нарушения псевдонимов. Если вы хотите T, вам нужно иметь T. Если ваш тип тривиально можно копировать, то, надеюсь, он также тривиально конструктивен, и нет никакой стоимости. В любом случае вам нужно скопировать возвращаемый операнд в возвращаемое значение функции, и эта копия будет удалена, поэтому здесь нет никакой дополнительной стоимости.

Ответ 2

Вы хотите использовать шаблон класса std::aligned_storage. Он был разработан для решения этой конкретной проблемы. Вот пример решения с некоторыми SFINAE, основанный на вашей проверке в вашем вопросе.

template<class T>
typename std::enable_if<std::is_trivially_copyable<T>::value, T>::type deserialize(const char *data) {
    typename std::aligned_storage<sizeof(T), alignof(T)>::type destination;
    std::memcpy(&destination, data, sizeof(T));
    return reinterpret_cast<T &>(destination);
}