提问人:Brad Spencer 提问时间:10/20/2023 最后编辑:Brad Spencer 更新时间:10/20/2023 访问量:96
我怎样才能访问同一个超大分配对象中的第二个不同的对象,只给出一个指向第一个对象的指针?
How can I access a second disparate object in the same over-sized allocated given only a pointer to the first?
问:
请考虑以下几点。
struct T { int a = 1; };
struct U { char b = 2; };
static_assert(alignof(U) == 1);
// Allocate storage. Cast via P0593 to access implicit array of unsigned char.
auto* s = reinterpret_cast<unsigned char*>(::operator new(sizeof(T) + sizeof(U)));
// Place T and U in that storage.
T* t = new (s) T;
U* u = new (s + sizeof(T)) U;
// Is there a legal way to get a pointer to `u` given only `t` and not `s`?
U* u2 = reinterpret_cast<U*>(reinterpret_cast<unsigned char*>(t) + sizeof(T));
问题:有没有办法,只是为了得到?t
u
我知道我能得到,而且大概我能得到。但是,如果我只有,有办法吗?std::launder()
s
t
std::launder()
s + sizeof(T)
u
t
我知道它对“可达性”有要求,所以我认为它不能成为解决方案的一部分,因为无法从 .std::launder()
u
t
我知道如果我把 T 和 U 放在一个结构中,情况会有所不同,但这不是我感兴趣的情况。(考虑到 U 实际上可能是动态大小的。
我认为我的问题归结为:如何从中恢复(底层完整存储)?我知道我可以在 处获得对象的对象表示,但我认为这与因为它只有很长不同,对吧?s
t
t
s
sizeof(T)
如果我想分配一次并存储这些非数组不同的对象,我是否被迫存储指向内部的指针只是为了以后恢复它?u
t
答:
2赞
user17732522
10/20/2023
#1
正如你所说,有一个明确禁止这样做的前提条件。这似乎是不可能的。std::launder
该语言中还有其他一些结构可以使可访问的字节数超过此前提条件所能达到的字节数,例如,请参阅我的问题 std::launder vs placement-new reachability condition 和 是否隐式对象创建规避了 std::launder 的可达性条件?。
但是,鉴于这些限制被明确添加到 ,我怀疑它们没有应用于这些其他结构是缺陷,而不是相反。std::launder
我不知道是否有任何编译器实际上利用了这种可访问性限制进行优化。
评论
0赞
Brad Spencer
10/20/2023
尽管进行了大量搜索,但我没有找到 stackoverflow.com/questions/76113351 这似乎是本质上是同一个问题。(尽管在我的情况下,存储阵列是隐式创建的。
0赞
Brad Spencer
10/20/2023
如果你的答案是正确的,这是否意味着旧的C技巧,即有一个“头结构”,然后是动态大小的“溢出”数据,在C++20中没有类似物?这是否意味着如果不存储指向它们的指针,您永远无法在结构之后访问这些“额外字节”?(对不起,如果这是一个单独的问题。
0赞
user17732522
10/20/2023
@BradSpencer 您仍然可以存储指向标头开始的基础存储的指针,并在访问标头时对其进行清洗。但除此之外,我想是的。不幸的是,我还没有找到任何关于这个先决条件背后的意图的讨论。它与指针值在 C++17 中的一般工作方式的重写一起添加,介绍这些更改的论文不包含任何讨论。据推测,它应该使编译器能够在不应用此类技巧的假设下进行优化,但我认为任何编译器实际上都不会假设给定的 C 兼容性。
0赞
James Kanze
10/20/2023
@BradSpencer 函数的实现通常会使用类似的技巧。但是,当然,此函数的实现可以使用非标准技巧,或利用特定编译器的已知行为。std::make_unique
0赞
user17732522
10/20/2023
@JamesKanze 需要这个技巧做什么?此外,它不能依赖这样的技巧,因为从 C++23 开始,它必须可用于禁止 UB 的常量表达式。因此,它甚至不能在其实现中使用(至少是路径)。std::make_unique
reinterpret_cast
if consteval
评论