在对象的低级创建/销毁下保证价值表示的保存(如果有)

问题描述 投票:0回答:1

他们经常向 SO 提出有关类型双关操作有效性的问题。例如,我最近贡献了一篇文章:Reinterpret_cast 从 char* 到 uint32_t* CPP 中的未定义行为?.

但是我在回答上述问题时想到了一个问题,我可以用这个片段来说明:

#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>

struct S {
    alignas(std::uint64_t) alignas(std::uint32_t)
        std::array<std::byte, sizeof(std::uint64_t)> storage = {some init values};
};

int main()
{
    S s;
    std::uint64_t* pi64 = std::start_lifetime_as<std::uint64_t>(s.storage.data());
    // implicitely creating an uint32_t at the same location, ending lifetime of the uint64_t there
    std::uint32_t* pi32 = std::launder(reinterpret_cast<std::uint32_t*>(s.storage.data()));
    // is there any guarantee that the value representation is unchanged?
    pi64 = std::start_lifetime_as<std::uint64_t>(s.storage);
    // is s.storage guaranteed to be unchanged?
}

到目前为止,这只是理论代码,因为据我所知,没有编译器支持

std::start_lifetime_as
。但我认为这有助于说明我的理解问题。

定义

pi64
时,我显式启动一个
std::uint64_t
对象,其值表示在字节级别上是我存储在字节数组中的值。

但随后我隐式创建了一个重叠的

std::uint32_t
,AFAIU,结束了上述 std::uint64_t
 对象的生命周期

底层字节会发生什么?标准规则是什么(我知道

unsigned char
/
std::byte
数组有特殊规则)?

c++ language-lawyer lifetime type-punning
1个回答
0
投票

但是我隐式地创建了一个重叠的

std::uint32_t

不,你不是。 您编写了

std::launder(reinterpret_cast<std::uint32_t*>(s.storage.data()))
,如果传入
std::uint32_t
的指针处没有
std::launder
对象,则这是未定义的行为(请参阅 std::launder
Precondition
)。

如果你改为这样写,你的例子就会有意义

std::uint32_t* pi32 = std::start_lifetime_as<std::uint32_t>(s.storage.data()));

底层字节会发生什么?

它们保持原样。 [obj.lifetime] p3 解释了对于

std::start_lifetime_as

[新对象]的对象表示是调用

start_lifetime_as
之前存储的内容。 [...] 除了不访问存储之外。

std::start_lifetime_as
不访问属于存储的任何字节;它纯粹可以看作是给编译器的指令。 因此,您可以根据需要将任意多个具有相同存储的
std::start_lifetime_as
调用链接在一起,并且对于相同类型,您将始终获得相同的值。

© www.soinside.com 2019 - 2024. All rights reserved.