C++23中如何让结构体末尾成员不占空间以引用后续结构体数组?
C++23 简洁实现方案
可以通过空嵌套结构体+[[no_unique_address]]+运算符重载的方式实现,既保证成员s不占用内存,又能直接通过&m->s获取后续Other数组的起始地址:
#include <cstdint> #include <memory> // 用于std::launder struct Other { // 示例成员,根据实际定义调整 uint32_t x; uint64_t y; }; struct Main { uint64_t a; uint64_t b; // 嵌套空结构体,无数据成员,仅提供运算符重载 [[no_unique_address]] struct { // 重载取地址运算符,返回后续第一个Other的地址 Other* operator&() noexcept { // this指向当前Main对象,this+1就是Main之后的内存地址 return std::launder(reinterpret_cast<Other*>(this + 1)); } const Other* operator&() const noexcept { return std::launder(reinterpret_cast<const Other*>(this + 1)); } // 重载->运算符,方便直接访问第一个Other的成员 Other* operator->() noexcept { return &(*this); } const Other* operator->() const noexcept { return &(*this); } // 重载转换运算符,直接返回第一个Other的引用 operator Other&() noexcept { return **this; } operator const Other&() const noexcept { return **this; } } s; };
方案说明
[[no_unique_address]]修饰空结构体:嵌套结构体无数据成员,属于空类,[[no_unique_address]]会触发空成员优化,让s不占用任何内存空间,Main的大小仍为sizeof(uint64_t)*2,完全兼容原有内存布局。- 重载
operator&():当取&m->s时,会返回Main对象之后的内存地址(即第一个Other的地址),无需手动强制转换,直接用于构造std::span:// 假设内存帧起始地址为frame_ptr,Other数组长度为count Main* m = static_cast<Main*>(frame_ptr); std::span<Other> other_span(&m->s, count); - 额外运算符重载:
operator->()和转换运算符可以让你直接通过m->s访问第一个Other的成员,比如m->s.x,进一步提升代码可读性。 std::launder的作用:确保指针指向的Other对象被正确识别,避免未定义行为(因为Other对象是在Main之后构造的,需要launder告知编译器该地址的对象已存在)。
优势对比
相比原有的双结构体转换方案,该方案:
- 无需维护两个重复的结构体,代码更简洁
- 直接通过成员
s操作,表达性更强 - 完全符合C++23标准,无依赖扩展特性
内容的提问来源于stack exchange,提问作者LeXav
相关产品推荐
相关产品推荐

