如何从char const*创建可平凡构造结构体且不调用默认构造函数?
如何从缓冲区创建对齐要求大于1的可平凡构造结构体且不调用默认构造函数?
给定一个对齐要求大于1的可平凡构造结构体,需要从缓冲区的二进制数据直接创建该结构体实例,同时满足两个要求:
- 不调用结构体的默认构造函数(避免默认初始化成员,比如示例中的
type枚举); - 不能直接通过
reinterpret_cast复制(因为缓冲区地址可能不满足结构体的对齐要求,直接解引用会触发未定义行为)。
示例代码:
enum class FoodType { Fruit, Veggie, Meat }; struct Banana { int64_t hp; int64_t deliciousness; FoodType type; };
需要实现如下函数:
Banana Read(char const* buffer) { return WHAT_DO_I_DO(buffer); }
可行方案
方案一:利用栈对齐变量 + std::memcpy
栈上的局部变量会自动满足结构体的对齐要求,我们可以先声明一个结构体对象(对于平凡类型,默认构造不会执行任何初始化操作,成员值为不确定状态),再用std::memcpy将缓冲区的字节数据直接覆盖到该对象上,最后返回。
#include <cstring> // 引入std::memcpy Banana Read(char const* buffer) { Banana result; std::memcpy(&result, buffer, sizeof(Banana)); return result; }
说明:
- 平凡类型的默认构造是“无操作”的,不会对成员进行任何初始化,完全避免了默认初始化
type的问题; std::memcpy是标准允许的操作:对于可平凡复制的类型,字节复制的结果等价于对象复制,不会触发未定义行为;- 栈变量自动对齐,规避了缓冲区地址不对齐的问题。
方案二:手动分配对齐内存(适用于严格避免栈变量默认构造的场景)
如果需要完全跳过栈变量的默认构造步骤,可以手动分配满足对齐要求的堆内存,复制数据后再取出对象:
#include <cstdlib> // 引入aligned_alloc、std::free #include <cstring> // 引入std::memcpy Banana Read(char const* buffer) { // 分配满足Banana对齐要求的内存 void* aligned_mem = aligned_alloc(alignof(Banana), sizeof(Banana)); if (!aligned_mem) { std::abort(); // 处理内存分配失败的情况 } // 复制缓冲区数据到对齐内存 std::memcpy(aligned_mem, buffer, sizeof(Banana)); // 将对齐内存中的对象复制到返回值 Banana result = *static_cast<Banana*>(aligned_mem); // 释放堆内存 std::free(aligned_mem); return result; }
方案三:C++20及以上使用std::bit_cast(间接方式)
std::bit_cast要求源和目标类型大小相同且均可平凡复制,我们可以先将缓冲区数据复制到一个同大小的std::array中,再通过bit_cast转换为目标结构体:
#include <cstring> #include <array> #include <bit> // C++20引入 Banana Read(char const* buffer) { std::array<char, sizeof(Banana)> buf; std::memcpy(buf.data(), buffer, sizeof(Banana)); return std::bit_cast<Banana>(buf); }
注意:这种方式本质上和方案一类似,只是借助std::bit_cast完成类型转换,同样需要确保结构体是可平凡复制的。
内容的提问来源于stack exchange,提问作者weineng
相关产品推荐
相关产品推荐

