从unique_ptr <t>的void *到T **的转换是如何工作的?

时间:2017-09-13 17:59:59

标签: c++ c++11 visual-c++ unique-ptr

我对VC ++ 2015中看到的结果感到惊讶,需要帮助了解它是如何工作的。

struct MyType
{
  MyType(int x_) : x(x_) { }
  int x;
};

auto u = std::make_unique<MyType>(10);
void* pv = &u;

这明显失败,因为u的地址不是指向MyType的指针:

MyType *pM = (MyType*)pv;

但是这可行,pM2获取MyType中存储的u对象的地址:

MyType** ppM = (MyType**)pv;
MyType* pM2 = *ppM;

标准中是否有任何内容表明这应该有效?或者它只是由于我的编译器的不可移植的实现细节而工作?是什么允许我将unique_ptr视为一轮中的指针指针?

在你说之前,&#34;那是愚蠢的,不要使用void*或C风格的演员&#34;,请理解我正在使用遗产通过void指针和结构成员的偏移来处理结构的序列化的代码。我现在无法改变这一部分。但我想使用unique_ptr作为结构成员来简化内存所有权和清理。我想知道我的unique_ptr在这个遗留环境中是多么脆弱。

3 个答案:

答案 0 :(得分:2)

这基本上只是你的幸运。

在特定编译器的ABI中,存储由T*维护的对象的unique_ptr是对象的第一个成员,因此它与对象本身具有相同的地址。与此示例大致相同:

struct container {
    int val;
};

int main() {
    container c{15};

    intptr_t val1 = reinterpret_cast<intptr_t>(&c);
    intptr_t val2 = reinterpret_cast<intptr_t>(&(c.val));

    assert(val1 == val2); //will pretty much always be true
}

当然,这不是您应该依赖的行为!标准未指定,如果供应商决定他们有更好的格式来存储指针{{} 1}}。

答案 1 :(得分:1)

基本上你正在做这样的事情:

Traceback (most recent call last):
  File "app.py", line 372, in <module>
    train(model_filename=args.model, epochs=args.epochs, dim=args.dim)
  File "app.py", line 61, in train
    output_classes=reader.CLASSES)
  File "/home/ubuntu/calypso_v2/model.py", line 53, in build_model
    net = Permute(3,2)(net)
TypeError: __init__() takes exactly 2 arguments (3 given)

有一些弯路和C风格的演员阵容。您将指针指向std::unique_ptr<MyType> up = ...; MyType* p = *reinterpret_cast<MyType**>(&up); 并将其重新解释为指针unique_ptr

的指针

这是纯粹的运气并导致未定义的行为,您不应该出于任何原因使用这种类型的代码。如果需要内部指针,请使用unique_ptr上的MyType方法。

答案 2 :(得分:0)

这是未定义的行为,恰好可以工作,因为唯一指针恰好只存储一个指针作为其状态,而该状态是指向T的指针。

未定义的行为可以执行任何操作,包括时间旅行和格式化硬盘驱动器。我知道有人说这个和其他人认为这是一个玩笑,但这些实际上是你可以通过实验验证的真实陈述。

碰巧,你的未定义行为已经以&#34;工作的方式重新解释了一些记忆。

您无法使用库以定义的方式序列化/反序列化非pod结构。你可以破解它的工作,但任何编译器更新(甚至编译器标志更新!)可能突然表现完全不同。

考虑使用一个用于序列化/反序列化的结构,另一个用于运行时使用。马歇尔从一个到另一个。是的,这很糟糕。