含匿名union的C++结构体编译失败,如何实现standard_layout与trivially_copyable?
需要设计一个可用于进程间消息队列收发的结构体,要求满足:固定长度、standard_layout、trivially_copyable。消息类型多样(定时器、控制、订单等),计划用匿名union承载不同消息体以简化成员访问(如msg.as_time而非msg.union_body.as_time),但使用std::chrono::system_clock::time_point作为union成员时,编译器报错无法生成默认构造函数,即使放弃trivially_copyable要求仍无法编译。
编译错误提示:
error: use of deleted function 'Message_t::Message_t()'
note: 'Message_t::Message_t()' is implicitly deleted because the default definition would be ill-formed:
union member 'Message_t::::as_time' with non-trivial 'constexpr std::chrono::time_point<_Clock, _Dur>::time_point() [with _Clock = std::chrono::_V2::system_clock; _Dur = std::chrono::duration<long int, std::ratio<1, 1000000000> >]'
问题原因
匿名union中包含的TimeStamp_t(即std::chrono::time_point)具有非平凡默认构造函数,导致编译器无法自动生成Message_t的默认构造函数,触发编译错误。
解决代码
手动为Message_t添加默认构造函数,显式初始化union中的一个成员:
#include <chrono> #include <iostream> using TimeStamp_t = std::chrono::system_clock::time_point; enum MsgType_e { Timer, Count, }; struct Message_t { union { TimeStamp_t as_time; int64_t as_int; }; MsgType_e type; // 显式提供默认构造函数,初始化time_point和消息类型 Message_t() : as_time{}, type(Timer) {} }; int main() { Message_t msg; std::cout << msg.as_time.time_since_epoch().count(); }
注意:此方案仅解决编译问题,但Message_t不再是trivially_copyable,不符合进程间通信的严格内存要求。
核心思路
std::chrono::time_point的非平凡构造函数会破坏trivially_copyable特性,因此直接存储其底层的数值表示(时间戳对应的时长计数),而非time_point对象本身,既保证内存布局精确,又满足所有要求。
代码实现
#include <chrono> #include <cstdint> #include <iostream> // 定义时间戳和对应时长类型 using TimeStamp_t = std::chrono::system_clock::time_point; using Duration_t = std::chrono::system_clock::duration; enum MsgType_e { Timer, Count, }; struct Message_t { union { int64_t as_time_raw; // 存储time_point的底层数值 int64_t as_int; // 整数类型消息 }; MsgType_e type; // 消息类型标记 }; // 辅助函数:将time_point写入Message_t void set_time_message(Message_t& msg, TimeStamp_t tp) { msg.type = Timer; msg.as_time_raw = std::chrono::duration_cast<Duration_t>(tp.time_since_epoch()).count(); } // 辅助函数:从Message_t读取time_point TimeStamp_t get_time_message(const Message_t& msg) { return TimeStamp_t(Duration_t(msg.as_time_raw)); } // 验证结构体特性 static_assert(std::is_standard_layout_v<Message_t>, "Message_t must be standard layout"); static_assert(std::is_trivially_copyable_v<Message_t>, "Message_t must be trivially copyable"); int main() { Message_t msg{}; // 支持默认构造 // 测试定时器消息 set_time_message(msg, std::chrono::system_clock::now()); std::cout << "Time stamp: " << get_time_message(msg).time_since_epoch().count() << std::endl; // 测试整数消息 msg.type = Count; msg.as_int = 123456; std::cout << "Integer value: " << msg.as_int << std::endl; }
方案说明
Message_t完全满足standard_layout和trivially_copyable要求,内存布局固定,适合进程间消息队列传输。- 通过辅助函数完成
time_point与底层数值的转换,保证访问逻辑清晰,同时避免非平凡类型破坏结构体特性。 - 匿名union的使用保持了成员访问的简洁性。
内容的提问来源于stack exchange,提问作者Leon

