Каков метод 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);
}