我可以 reinterpret_cast POD C 数组的某些字节范围到 std::array<char,N> 吗?

Can I reinterpret_cast some byte range of a POD C-Array to std::array<char,N>?

我想使用长字节数组 s 的固定连续字节作为 std::map<std::array<char,N>,int> 中的键。 我可以不通过将 s 的子数组重新解释为 std::array<char,N> 来进行复制吗?

这是一个最小的例子:

#include <map>
int main() {
    std::map<std::array<char,10>,int> m;
    const char* s="Some long contiguous data";

    // reinterpret some contiguous 10 bytes of s as std::array<char,10>
    // Is this UB or valid? 
    const std::array<char,10>& key=*reinterpret_cast<const std::array<char,10>*>(s+5);

    m[key]=1;
}

我会说是的,因为 char 是一种不需要与特定地址对齐的 POD 类型(与更大的 POD 类型相反,请参阅 )。因此,只要覆盖的字节仍然是 s 的子范围,从每个地址开始 reinterpret_caststd::array<char,N> 应该没问题,即只要我确保我没有缓冲区溢出。

我真的可以这样做吗reinterpret_cast还是UB?

编辑: 在评论中,人们正确地指出了这样一个事实,即我不能确定 std::array<char,10> arr 它认为 (void*)&arr==(void*)&arr[0] 由于可能填充 [=25 的内部 c 数组数据成员=] 模板 class,尽管这通常不应该是这种情况,特别是因为我们正在考虑 char POD 阵列。所以我更新了我的问题:

当我通过 static_assert 检查确实没有填充时,我可以依赖上面所做的 reinterpret_cast 吗?当然,代码不会再在有填充的 compiler/platform 组合上编译,所以我不会使用这种方法。但我想知道:除了填充之外还有其他问题吗?或者代码是否通过 static_assert 检查有效?

否 - 在该地址没有 object 类型 std::array<char,10>,无论该类型的布局如何。 (char 的特殊规则不适用于碰巧有 char 子对象的类型。)一如既往,未定义行为的不是 reinterpret_cast 本身,而是 access 当使用它作为 map 键时,通过那个非对象。 (在这种情况下,您可以做的只是将其转换回真实类型,以便与需要固定指针类型但实际上不使用该对象的类 C 接口一起使用。)

这种访问当然也涉及到复制;如果您的目标是完全避免复制,只需制作一个

std::map<const char*,int,ten_cmp>

其中 ten_cmp 是一个 函子 类型,它比较从每个地址开始的 10 个字节(通过 std::strncmpstd::string_view)。

如果您确实希望 map 拥有其密钥数据,只需 std::memcpy 从字符串转换为密钥即可;编译器经常认识到这样的临时“缓冲区”不需要独立存在并且实际上以您希望使用 reinterpret_cast.[=22= 的方式从 source 中读取]