在cppref,我看到一个奇怪的类型特质检查器:std::has_unique_object_representations
从描述中,我无法想象使T
为std::has_unique_object_representations<T>::value
的任何类型false
。
有没有反例?
答案 0 :(得分:11)
了解此特征的目的需要理解对象“值表示”与其“对象表示”之间的区别。从标准:
类型为
T
的对象的对象表示是由T
类型的对象占用的N个无符号字符对象的序列,其中N等于{{1 }}。对象的值表示是保存类型sizeof(T)
的值的位集。对于简单的可复制类型,值表示是对象表示中的一组位,用于确定值,该值是实现定义的值集的一个离散元素。
因此,对象表示是对象的总存储量。但请考虑以下对象:
T
在许多系统上,struct T
{
char c;
int i;
};
将是8.为什么?因为sizeof(T)
必须是4字节对齐的,所以编译器在int
和c
之间插入3个字节的填充。由于这三个字节是对象存储的一部分(基于i
),因此它们是对象对象表示的一部分。
但是这三个字节不是其值表示的一部分。如果您修改了这些字节,它们就不会影响sizeof(T)
或c
的值,也不会影响其他任何内容。
如果您为i
写了operator==
重载,则对这些字节的更改不会影响其结果。这也意味着如果你写了一个T
重载,就不能这样实现:
operator==
为什么不呢?因为两个bool operator==(const T &lhs, const T &rhs)
{
return std::memcmp(&lhs, &rhs, sizeof(T)) == 0;
}
s对于这些填充字节可以具有不同的值,但仍具有相同的T
和c
值。因此它们具有相同的值表示,因此应被视为等效。
i
的对象表示及其值表示彼此完全重叠时(以及has_unique_object_representations
可以轻易复制时), T
为真。所以,你什么时候关心这个?
您可以通过将其值表示形式读取为字节数组并对其进行散列来编写适用于任何简单可复制类型T
的通用散列函数。好吧,你可以这样做,但前提是该类型没有填充字节。这就是T
告诉你的:对象表示中的所有字节都很重要。
另外,请注意,has_unique_object_representations
类型不一定具有此值,因为二进制相等和浮点相等在IEEE-754中不相同。所以包含float
s的类型也不一定是真的。实际上,使用一元补码有符号整数或带有陷阱表示的有符号整数的实现也不适用于这类类型。