这两个比较表达式相同吗?

Are these two comparison expressions identical?

本文关键字:表达式 比较 两个      更新时间:2023-10-16

关于这两个函数是否具有相同的逻辑,似乎有一些分歧:

bool operator<(const screenPoint& left, const screenPoint& right){
  if (left.x < right.x) return true;
  else return left.y < right.y;
}
bool operator<(const screenPoint& left, const screenPoint& right){
  return left.x < right.x || left.y < right.y;
}

我盯着它们看了很长一段时间,我看不出它们的行为会有什么不同。在两者中,如果是left.x < right.x,它们都返回true。如果不是,那么它们都返回left.y < right.y的结果。对吧?

如果没有,谁能详细说明一下。

另外,有人认为第一个与std::tie中词法排序的实现相同,这是真的吗?

这两个是完全相同的(如果你在第一个if中修复了缺少的括号),但是它们没有实现严格的弱排序,所以你可能不想使用它们(在标准有序的容器和算法中使用它们是无效的)。

证明:考虑a=(1,3) b=(2,2)的原关系。然后是<A.(违反非对称属性)>

正确的字典顺序应该是这样的:

bool operator<(const screenPoint& left, const screenPoint& right){
    if (left.x < right.x) 
        return true;
    if (left.x > right.x)
        return false;
    return left.y < right.y;
}

如果成员只有<操作符而没有>操作符,则将left.x > right.x替换为!(right.x < left.x)。这就是std::pairstd::tuple(由std::tie返回)实现字典比较的方式。

不要只是猜测,让你的编译器发出asm代码来检查差异。我的编译器(clang)对两个版本产生相同的结果:

#include <tuple>
__attribute__((noinline)) bool operatorLess1(const screenPoint& left, const screenPoint& right)
{
  if (left.x < right.x) return true;
  else return (left.y < right.y);
}
__attribute__((noinline)) bool operatorLess2(const screenPoint& left, const screenPoint& right) 
{
  return left.x < right.x || left.y < right.y;
}
__attribute__((noinline)) bool operatorLess3(const screenPoint& left, const screenPoint& right) 
{
  return std::tie(left.x, left.y) < std::tie(right.x, right.y);
}
__attribute__((noinline)) bool operatorLess4(const screenPoint& left, const screenPoint& right)
{
  if (left.x < right.x) return true;
  else if (left.x > right.x) return false;
  else return (left.y < right.y);
}
编制

clang++ -S -masm=intel -std=c++11 -O2 a.cpp

收益率:

__Z13operatorLess1RK11screenPointS1_:   ## @_Z13operatorLess1RK11screenPointS1_
    push    rbp
    mov rbp, rsp
Ltmp2:
    movss   xmm0, dword ptr [rsi]   ## xmm0 = mem[0],zero,zero,zero
    mov al, 1
    ucomiss xmm0, dword ptr [rdi]
    ja  LBB0_2
    movss   xmm0, dword ptr [rsi + 4] ## xmm0 = mem[0],zero,zero,zero
    ucomiss xmm0, dword ptr [rdi + 4]
    seta    al
LBB0_2:
    pop rbp
    ret
    .cfi_endproc

__Z13operatorLess2RK11screenPointS1_:   ## @_Z13operatorLess2RK11screenPointS1_
    .cfi_startproc
    push    rbp
    mov rbp, rsp
Ltmp5:
    movss   xmm0, dword ptr [rsi]   ## xmm0 = mem[0],zero,zero,zero
    mov al, 1
    ucomiss xmm0, dword ptr [rdi]
    ja  LBB1_2
    movss   xmm0, dword ptr [rsi + 4] ## xmm0 = mem[0],zero,zero,zero
    ucomiss xmm0, dword ptr [rdi + 4]
    seta    al
LBB1_2:
    pop rbp
    ret
    .cfi_endproc

__Z13operatorLess3RK11screenPointS1_:   ## @_Z13operatorLess3RK11screenPointS1_
    push    rbp
    mov rbp, rsp
Ltmp8:
    movss   xmm0, dword ptr [rdi]   ## xmm0 = mem[0],zero,zero,zero
    movss   xmm1, dword ptr [rsi]   ## xmm1 = mem[0],zero,zero,zero
    mov al, 1
    ucomiss xmm1, xmm0
    ja  LBB2_4
    ucomiss xmm0, xmm1
    jbe LBB2_3
    xor eax, eax
    pop rbp
    ret
LBB2_3:
    movss   xmm0, dword ptr [rsi + 4] ## xmm0 = mem[0],zero,zero,zero
    ucomiss xmm0, dword ptr [rdi + 4]
    seta    al
LBB2_4:                                 ## %_ZNSt3__1ltIJRKfS2_EJS2_S2_EEEbRKNS_5tupleIJDpT_EEERKNS3_IJDpT0_EEE.exit
    pop rbp
    ret
    .cfi_endproc
__Z13operatorLess4RK11screenPointS1_:   ## @_Z13operatorLess4RK11screenPointS1_
    .cfi_startproc
    push    rbp
    mov rbp, rsp
Ltmp11:
    movss   xmm0, dword ptr [rdi]   ## xmm0 = mem[0],zero,zero,zero
    movss   xmm1, dword ptr [rsi]   ## xmm1 = mem[0],zero,zero,zero
    mov al, 1
    ucomiss xmm1, xmm0
    ja  LBB3_4
    ucomiss xmm0, xmm1
    jbe LBB3_3
    xor eax, eax
    pop rbp
    ret
LBB3_3:
    movss   xmm0, dword ptr [rsi + 4] ## xmm0 = mem[0],zero,zero,zero
    ucomiss xmm0, dword ptr [rdi + 4]
    seta    al
LBB3_4:
    pop rbp
    ret
    .cfi_endproc

可以看到,前两个版本是相同的,它们将比较x或直接比较y。相反,正如interjay所指出的,std::tie版本是一个完全弱排序,当left.x > left.y返回0。实际上,它的代码与operatorLess4相同。