什么时候可以将结构安全地散列为字节数组?
When can a struct safely be hashed as an array of bytes?
对于相等意味着相同的结构 most-derived 每个数据成员的类型和字节相等,如果有的话,何时可以将结构安全地散列为字节数组?
这份文件
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3333.html
标题 "Hashing objects as byte arrays" 表明带有任何填充的结构不能安全地散列为字节数组。
是否需要对填充进行显式测试才能将结构安全地散列为字节数组?够了吗?
如果是这样,下面的草图是否恰当地说明了该测试?
#include <cstddef>
#include <iostream>
struct A
{
int i;
float f;
char c;
};
// hashing would start at offs_i (possibly hopping over a v-table) and end at
// offs_c + sizeof(char)
int main()
{
const std::size_t size_A = sizeof(A);
const std::size_t size_int = sizeof(int);
const std::size_t size_float = sizeof(float);
const std::size_t size_char = sizeof(char);
const std::size_t offs_i = offsetof(A, i);
const std::size_t offs_f = offsetof(A, f);
const std::size_t offs_c = offsetof(A, c);
bool padded = false;
if (offs_f != size_int)
padded = true;
else if (offs_c != size_int + size_float)
padded = true;
std::cout << "padded? " << std::boolalpha << padded << std::endl;
}
如果一个结构确实有填充,有什么方法可以解决以允许散列为字节数组,例如zero-ing 出填充位?
"Safe" 这里表示两个比较相等的结构将散列为相同的值。
几乎从来没有。该标准没有定义继承是如何实现的,例如使用 vtable 指针,因此不能保证两个 类 具有相等的最派生类型将具有任何特定于实现的继承相关数据相等。
此外,因为它们不是 PODs,offsetof 不能保证工作或产生有意义的结果 - 我相信它实际上只是普通的 UB。
总而言之,不要试图将结构视为字节数组因为它们不是。
至于他在报纸上的意图,这更可能是对委员会中一些狂犬病 "er mah gerd performances!" 狗的让步,而不是他真正想做的事情。
您引用的论文大致说明了要求:
is_trivially_copyable<T>::value &&
is_standard_layout<T>::value &&
is_contiguous_layout<T>::value
必须递归地对结构本身和所有成员成立。
前两个检查已经是标准库的一部分,您可以自己实现is_contiguous_layout<T>::value
。作为基础,将其成员的大小总和与结构本身的大小进行比较就足够了。我不认为,检查偏移量实际上是必要的。
如果您的类型仅由整数类型组成,这当然应该适用于 "the usual" 平台(CHAR_BIT == 8
,2-补码)。但是,我不确定它是否也适用于布尔值和浮点数,因为我相信该标准并未强制要求变量值与其位表示之间的唯一双向映射。
编辑:我刚刚意识到你无法满足条件 of same most derived type
,如果在派生的 class 中没有添加任何成员,或者如果你比较两个不同的 classes碰巧有相同的成员。所以你必须单独考虑类型。
如果结构是 POD,您应该能够在初始化时将整个结构的 bzero 或 memcpy 设置为 0,然后散列为字节数组应该没问题,并且高效。
对于相等意味着相同的结构 most-derived 每个数据成员的类型和字节相等,如果有的话,何时可以将结构安全地散列为字节数组?
这份文件
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3333.html
标题 "Hashing objects as byte arrays" 表明带有任何填充的结构不能安全地散列为字节数组。
是否需要对填充进行显式测试才能将结构安全地散列为字节数组?够了吗?
如果是这样,下面的草图是否恰当地说明了该测试?
#include <cstddef>
#include <iostream>
struct A
{
int i;
float f;
char c;
};
// hashing would start at offs_i (possibly hopping over a v-table) and end at
// offs_c + sizeof(char)
int main()
{
const std::size_t size_A = sizeof(A);
const std::size_t size_int = sizeof(int);
const std::size_t size_float = sizeof(float);
const std::size_t size_char = sizeof(char);
const std::size_t offs_i = offsetof(A, i);
const std::size_t offs_f = offsetof(A, f);
const std::size_t offs_c = offsetof(A, c);
bool padded = false;
if (offs_f != size_int)
padded = true;
else if (offs_c != size_int + size_float)
padded = true;
std::cout << "padded? " << std::boolalpha << padded << std::endl;
}
如果一个结构确实有填充,有什么方法可以解决以允许散列为字节数组,例如zero-ing 出填充位?
"Safe" 这里表示两个比较相等的结构将散列为相同的值。
几乎从来没有。该标准没有定义继承是如何实现的,例如使用 vtable 指针,因此不能保证两个 类 具有相等的最派生类型将具有任何特定于实现的继承相关数据相等。
此外,因为它们不是 PODs,offsetof 不能保证工作或产生有意义的结果 - 我相信它实际上只是普通的 UB。
总而言之,不要试图将结构视为字节数组因为它们不是。
至于他在报纸上的意图,这更可能是对委员会中一些狂犬病 "er mah gerd performances!" 狗的让步,而不是他真正想做的事情。
您引用的论文大致说明了要求:
is_trivially_copyable<T>::value &&
is_standard_layout<T>::value &&
is_contiguous_layout<T>::value
必须递归地对结构本身和所有成员成立。
前两个检查已经是标准库的一部分,您可以自己实现is_contiguous_layout<T>::value
。作为基础,将其成员的大小总和与结构本身的大小进行比较就足够了。我不认为,检查偏移量实际上是必要的。
如果您的类型仅由整数类型组成,这当然应该适用于 "the usual" 平台(CHAR_BIT == 8
,2-补码)。但是,我不确定它是否也适用于布尔值和浮点数,因为我相信该标准并未强制要求变量值与其位表示之间的唯一双向映射。
编辑:我刚刚意识到你无法满足条件 of same most derived type
,如果在派生的 class 中没有添加任何成员,或者如果你比较两个不同的 classes碰巧有相同的成员。所以你必须单独考虑类型。
如果结构是 POD,您应该能够在初始化时将整个结构的 bzero 或 memcpy 设置为 0,然后散列为字节数组应该没问题,并且高效。