我可以 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_cast
到 std::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::strncmp
或 std::string_view
)。
如果您确实希望 map
拥有其密钥数据,只需 std::memcpy
从字符串转换为密钥即可;编译器经常认识到这样的临时“缓冲区”不需要独立存在并且实际上以您希望使用 reinterpret_cast
.[=22= 的方式从 source 中读取]
我想使用长字节数组 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_cast
到 std::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::strncmp
或 std::string_view
)。
如果您确实希望 map
拥有其密钥数据,只需 std::memcpy
从字符串转换为密钥即可;编译器经常认识到这样的临时“缓冲区”不需要独立存在并且实际上以您希望使用 reinterpret_cast
.[=22= 的方式从 source 中读取]