从std :: aligned_union_t获取指向包含对象的指针

时间:2017-12-29 17:23:57

标签: c++

我想在std::aligned_union_t内使用placement-new构造任意类型的对象。一旦构造成功,我希望能够返回指向构造对象的指针,而无需单独存储它。只要我reinterpret_cast std::aligned_union_t,只要我确保将其转换为构造的原始类型,这样做是否合法?

以下代码是上述例证,合法吗?是否存在MyStruct应满足的任何类型特征要求?例如,它必须是POD吗?

#include <type_traits>
#include <memory>
#include <cstddef>
#include <exception>

struct MyStruct
{
    int value = 0;
};

constexpr size_t c_alignedUnionSize = 10;
std::aligned_union_t<c_alignedUnionSize, std::max_align_t> g_storage;

MyStruct* GetPtr()
{
    return reinterpret_cast<MyStruct*>(std::addressof(g_storage));
}

void Construct()
{
    if (sizeof(MyStruct) > sizeof(g_storage))
    {
        std::terminate();
    }

    auto ptr = new (std::addressof(g_storage)) MyStruct{};
    if (!ptr)
    {
        std::terminate();
    }

    GetPtr()->value = 123;
}

void Destroy()
{
    GetPtr()->~MyStruct();
}

int GetValue()
{
    return GetPtr()->value;
}

int main()
{
    Construct();
    auto value = GetValue();
    Destroy();
    return value;
}

2 个答案:

答案 0 :(得分:1)

reinterpret_cast这里应该是安全的。最新的标准草案说:

  

[expr.reinterpret.cast]

     

可以将对象指针显式转换为不同类型的对象指针。当对象指针类型的prvalue v转换为对象指针类型“指向cv T的指针”时,结果为static_­cast<cv T*>(static_­cast<cv void*>(v))。 [注意:将“指向T1”的类型的prvalue转换为“指向T2”的类型(其中T1T2是对象类型以及对齐要求的位置T2的{​​{1}}并不比T1更严格,并且返回其原始类型会产生原始指针值。 - 结束说明]

相关问题:

答案 1 :(得分:1)

不幸的是,标准禁止这样做。在C ++标准reinterpret_cast中,从一个指针到一个对象一个到另一个对象 b ,只有当两个对象是指针可互换,[basic.compound]/4

  

如果出现以下情况,则两个对象a和b是指针可互换的

     
      
  • 它们是同一个对象,或

  •   
  • 一个是union对象,另一个是该对象的非静态数据成员([class.union]),或者

  •   
  • 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则该对象的第一个基类子对象([class.mem]),或

  •   
  • 存在一个对象c,使得a和c是指针可互换的,而c和b是指针可互换的。

  •   
     

如果两个对象是指针可互换的,那么它们具有相同的地址,并且可以通过reinterpret_­cast从指向另一个的指针获得一个指针。 [注意:数组对象及其第一个元素不是指针可互换的,即使它们具有相同的地址。 - 结束说明]

指针具有正确的类型和正确的值(内存地址)这一事实并不能使其成为有效的指针。这种令人惊讶的行为的典型例子是:

alignas(int) unsigned char buff[2*sizeof(int)];
auto p1 = new(buff) int{};
auto p2 = new(buff+sizeof(int)) int{};
*(p1+1) = 10;//Undefined behavior
//p1+1 does not point to *p2 even if p1 and p2 have same type and value.

因此,要符合标准,您必须存储new返回的指针的值。

我找到了一个很好的解决方案,包括将指针转换为整数类型,并将其转换为其他指针类型,这将导致实现定义的行为[expr.reinterpret_cast]/5):

reinterpret_cast<MyStruct*>(reinterpret_cast<std::uintptr_t>(addressof(g_storage));