std::bit_cast 生成多个值的值表示的示例是什么?

Kae*_*Rin 4 c++ language-lawyer c++20

以下是引用自[bit.cast]下的标准(草案n4861)(强调的是我的)

返回:类型的对象To。隐式创建嵌套在结果中的对象 (6.7.2)。结果的值表示的每一位都等于 的对象表示中的相应位from。结果的填充位未指定。对于结果和其中创建的每个对象,如果没有与所产生的值表示对应的对象类型的值,则行为是未定义的。如果有多个这样的值,生成哪个值是未指定的

所以我的问题是,std::bit_cast生成对应于多个不同值的值表示的场景的示例是什么?

Sto*_*ica 8

总和类型浮现在脑海中。联合可以有相同类型的成员:

union u {
    int a;
    int b;
};
Run Code Online (Sandbox Code Playgroud)

现在,如果我们将一些东西位投射到 a 中uu我们会得到什么样的结果?它是一个a活跃的地方还是一个b?这些是 a 形式上不同的值u,但它们似乎具有相同的值表示。


Art*_*yer 5

在最初的提案中,该段落写为:http://wg21.link/P0476r1

返回: 类型的对象To,其对象表示形式等于 的对象表示形式From。如果多个对象表示可以表示的值表示From,则未指定To返回哪个值。如果没有值表示对应于To对象表示,则返回值未指定。

所以看起来意图是允许改变填充位。也就是说,具有From不同填充的相同值表示有多种可能的对象表示,因此具有To不同值的多种可能值表示。例如,当bit_cast像这样:

struct foo {
    std::uint8_t a;
    // 1 byte padding to align
    std::uint16_t b;
};

bit_cast<std::uint32_t>(foo_value);
Run Code Online (Sandbox Code Playgroud)

填充字节允许不同(因此bit_cast允许多个 s 返回不同的值,但在这种情况下,往返位转换将保存值)


但似乎有些情况会破坏往返。考虑这种情况(https://godbolt.org/z/KGTGes):

int a[1];
int b[1];
assert(std::begin(a) == std::end(b));  // 1

std::uintptr_t a_bits = std::bit_cast<std::uintptr_t>(std::begin(a));
std::uintptr_t b_bits = std::bit_cast<std::uintptr_t>(std::end(b));

assert(a_bits == b_bits);  // 2
Run Code Online (Sandbox Code Playgroud)

第一个断言被允许通过(并且在未优化的构建中也是如此)。当它确实过去时,第二个通常也会过去。当bit_cast回到 时int*,什么索引应该有效?[-1]获取b[0](如果是std::end(b)) 的值,或者0(a[0]如果是std::begin(a)) 的值?好像没有具体说明


在其他情况下,指针可以具有相同的值但不同。对于 array T[N],将其地址 a 转换为 与T(*)[N]将指针转换为void*第一个元素 a 具有相同的值。将空类成员与 一起使用时也会发生同样的情况。T*void*[[no_unique_address]]

  • _看起来意图是允许更改填充位_填充位是对象表示的一部分,您的引用说_类型为To的对象,其对象表示**等于**From_的对象表示。所以填充位原本是不能改变的。仅更改填充位不会更改该值。但指针是具有相同值表示的多个值的一个很好的例子。 (2认同)