C++ gcc:通过显式memcpy避免严格的别名冲突警告

C++ gcc:通过显式memcpy避免严格的别名冲突警告,c++,gcc,c++17,gcc-warning,strict-aliasing,C++,Gcc,C++17,Gcc Warning,Strict Aliasing,我有一个需要64位内存的类。为了实现平等,我使用了reinterpret_cast,但它在gcc 7.2(但不是clang 5.0)上产生了此警告: 通过memcpy显式复制数据来避免强制转换,可防止出现警告。据我所知,它也应该是便携式的 查看汇编程序(gcc 7.2和-std=c++17-O3),memcpy得到了完美的优化,而直接的比较会导致效率较低的代码: via_memcpy(X, X): cmp rdi, rsi sete al ret via_cast(X, X):

我有一个需要64位内存的类。为了实现平等,我使用了
reinterpret_cast
,但它在gcc 7.2(但不是clang 5.0)上产生了此警告:

通过
memcpy
显式复制数据来避免强制转换,可防止出现警告。据我所知,它也应该是便携式的

查看汇编程序(gcc 7.2和
-std=c++17-O3
),memcpy得到了完美的优化,而直接的比较会导致效率较低的代码:

via_memcpy(X, X):
  cmp rdi, rsi
  sete al
  ret

via_cast(X, X):
  cmp rdi, rsi
  sete al
  ret

via_comparisons(X, X):
  xor eax, eax
  cmp esi, edi
  je .L7
  rep ret
.L7:
  sar rdi, 32
  sar rsi, 32
  cmp edi, esi
  sete al
  ret
与clang 5.0非常相似(
-std=c++17-O3
):

从这个实验来看,
memcpy
版本似乎是代码中性能关键部分的最佳方法

问题:

  • 我的理解是正确的,<代码> MeMCPY < /Cord>版本是可移植的C++代码?< /LI>
  • 假设编译器能够像本例中那样优化
    memcpy
    调用是否合理
  • 有没有我忽略的更好的方法
更新:

正如UKMonkey所指出的,
memcmp
在进行位比较时更为自然。它还可编译为相同的优化版本:

bool eq_via_memcmp(X x) {
    return std::memcmp(this, &x, sizeof(*this)) == 0;
}
这是你的电话号码。还应该是可移植的(
sizeof(*this)
是64位的),因此我认为它是迄今为止最好的解决方案。

在C++17中,与结合使用可以使用:

bool eq_via_memcmp(X x) {
    static_assert(std::has_unique_object_representations_v<X>);
    return std::memcmp(this, &x, sizeof(*this)) == 0;
}
静态断言确保类
X
不包含填充位。否则,比较两个逻辑上等价的对象可能会返回false,因为填充位的内容可能不同。在这种情况下,在编译时拒绝该代码更安全


(注意:C++20可能会添加,它可以作为
memcmp
的替代品。但是,您仍然必须确保没有出于同样的原因涉及填充。)

您依赖于内存中的特定结构布局,所以这大概会限制可移植性。所以你真正想要做的是对类的内存进行位比较;正好是64位?为什么不使用
memcmp(this,other,sizeof(X))
?如果您想这样做,我建议至少断言
具有唯一的对象表示形式。\u v
。请注意,一般来说,对结构使用memcmp是不安全的-它没有考虑填充位/字节的不确定值。我们可能会学习C++20。
via_memcpy(X, X): # @via_memcpy(X, X)
  cmp rdi, rsi
  sete al
  ret

via_cast(X, X): # @via_cast(X, X)
  cmp rdi, rsi
  sete al
  ret

via_comparisons(X, X): # @via_comparisons(X, X)
  cmp edi, esi
  jne .LBB2_1
  mov rax, rdi
  shr rax, 32
  mov rcx, rsi
  shr rcx, 32
  shl eax, 16
  shl ecx, 16
  cmp ecx, eax
  jne .LBB2_3
  shr rdi, 48
  shr rsi, 48
  shl edi, 16
  shl esi, 16
  cmp esi, edi
  sete al
  ret
.LBB2_1:
  xor eax, eax
  ret
.LBB2_3:
  xor eax, eax
  ret
bool eq_via_memcmp(X x) {
    return std::memcmp(this, &x, sizeof(*this)) == 0;
}
bool eq_via_memcmp(X x) {
    static_assert(std::has_unique_object_representations_v<X>);
    return std::memcmp(this, &x, sizeof(*this)) == 0;
}
via_memcmp(X, X):
  cmp rdi, rsi
  sete al
  ret