首页 > 解决方案 > 在 C++20 中,我们可以将标头和数据对象打包在一起,并通过偏移量从标头访问数据吗?

问题描述

struct header
{
    int a1;
    int a2;
    // ...;

    std::byte * get_data_bytes()
    {
        return align_up<data>( // make sure alignment requirements are met
                    reinterpret_cast<std::byte *>(this) + sizeof(*this)); 
       // maybe std::launder around the reinterpret_cast (only) is needed?
    }

    data & get_data()
    {
        return *std::launder(reinterpret_cast<data *>(get_data_bytes()));
    }

    void use_data()
    {
        get_data().use();
    }
};

void example()
{
    alignas(header) std::byte storage[/* plenty of space*/]; 
    
    auto h = new (storage) header;
    new (h->get_data_bytes()) data;
    
    h->use_data(); // Does this eventually cause a UB?
}

没有UB这可能吗?如果没有,是否有替代方案?要求是数据不是标头的子对象,并且标头中没有指向数据的指针/引用以避免额外的间接性。这可能通过灵活的空数组实现,但我认为这些不在标准中。

标签: c++c++20

解决方案


唯一甚至假设是 UB 是是否reinterpret_cast<std::byte *>(this)是指向数组中字节之一的指针。但是你可以std::launder确保它是。事实上,对字节指针进行清洗非常有用,您可以为它们创建模板函数:

template<typename T>
std::byte *to_byte_ptr(T *ptr)
{
  return std::launder(reinterpret_cast<std::byte*>(ptr));
}

template<typename T>
T *from_byte_ptr(std::byte *ptr)
{
  return std::launder(reinterpret_cast<T*>(ptr));
}

其他一切都很好。您header在由字节数组提供的存储中创建,因此std::byte可以通过launder. 而且由于这些std::byte对象位于数组中,因此您可以对它们进行指针运算。虽然这些std::byte对象实际上并不指向它们为其提供存储的对象,但如果您有一个与所需对象具有相同地址的指针,您可以launder检索指向该对象的指针。


推荐阅读