我对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
在这个遗留环境中是多么脆弱。
答案 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结构。你可以破解它的工作,但任何编译器更新(甚至编译器标志更新!)可能突然表现完全不同。
考虑使用一个用于序列化/反序列化的结构,另一个用于运行时使用。马歇尔从一个到另一个。是的,这很糟糕。