什么类型会使"std :: has_unique_object_representations"返回false?

xml*_*lmx 8 c++ standards type-traits c++17

cppref,我看到一个奇怪的类型特征检查器:std::has_unique_object_representations

从它的描述,我无法想象任何类型T,使std::has_unique_object_representations<T>::valuefalse.

有没有反例?

Nic*_*las 11

理解这个特征的目的需要理解对象"值表示"和它的"对象表示"之间的区别.从标准:

类型对象的对象表示是由类型T的对象占据的N个无符号char对象的序列T,其中N等于sizeof(T).对象的值表示是保存type值的位集T.对于简单的可复制类型,值表示是对象表示中的一组位,用于确定值,该值是实现定义的值集的一个离散元素.

因此,对象表示是对象的总存储.但请考虑以下对象:

struct T
{
  char c;
  int i;
};
Run Code Online (Sandbox Code Playgroud)

在许多系统上,sizeof(T)将是8.为什么?因为int必须是4字节对齐,所以编译器在c和之间插入3个字节的填充i.由于这三个字节是对象存储的一部分(基于sizeof(T)),因此它们是对象的对象表示的一部分.

但这三个字节不是其值表示的一部分.如果您修改了这些字节,它们不会影响cor 的值i或其他任何值.

如果你写了一个operator==重载T,那些字节的更改不会影响它的结果.这也意味着如果你写了一个operator==重载,它就不能像这样实现:

bool operator==(const T &lhs, const T &rhs)
{
  return std::memcmp(&lhs, &rhs, sizeof(T)) == 0;
}
Run Code Online (Sandbox Code Playgroud)

为什么不?因为两个Ts对于那些填充字节可以具有不同的值,但仍然具有相同的c和的值i.因此它们具有相同的值表示,因此应被视为等效.

has_unique_object_representationsT对象表示及其值表示彼此完全重叠时(以及何时T可以轻易复制),则为true .所以,你什么时候关心这个?

您可以T通过将其值表示形式读取为字节数组并对其进行散列来编写适用于任何普通可复制类型的通用散列函数.好吧,你可以这样做,但只有当类型没有填充字节时.这就是has_unique_object_representations告诉你的:对象表示中的所有字节都很重要.

另外,请注意,float类型不一定是真值,因为二进制相等和浮点相等在IEEE-754中不一样.所以包含floats的类型也不一定是真的.实际上,使用一元补码有符号整数或带陷阱表示的有符号整数的实现对于这些类型也不会如此.