为什么 std::tuple 会调用两次 operator <=> 运算符?

9
以下代码两次调用了operator <=>,参数顺序相反。为什么?
GCC 10.2和clang 12似乎都使用libstdc++-10,它的提供了operator <=>,因此似乎不是缺少标准库支持的情况,我的代码必须是错误的。如何修复它?
#include <tuple>
#include <compare>
#include <iostream>

struct X {
    int i;
    auto operator <=>(X const& other) const {
        std::cout << this << " <=> " << &other << std::endl;
        return i <=> other.i;
    }
};

int main() {
    std::tuple{X{42}} <=> std::tuple{X{42}};
}

@IgorR. 你是在暗示X不是三向可比的吗? - Roman Odaisky
这不是可以预料的吗?它必须返回三个中的一个:a < bb < a 或相等?例如,return a < b ? -1 : b < a ? 1 : 0,在三分之二的情况下也需要进行两次比较。 - Ted Lyngmo
2
@TedLyngmo 但是 X::operator <=> 可以在一次调用中回答这个问题,这也是它存在的全部原因。 - Roman Odaisky
@TedLyngmo,OP 中的自有实现使用运算符<=>。运算符 <、> 等并没有被移除。这些从元组实现中移除了,只剩下<=> - armagedescu
3
嗯,是的,你们所有人可能都是正确的。 :-) 我会回去做晚饭了。 - Ted Lyngmo
显示剩余5条评论
1个回答

5
简短回答:您需要为X定义operator==。
std::tuple通过合成的三向比较比较元素,只有当类型满足std::three_way_comparable_with时才使用<=>。最终,这需要std::three_way_comparable,这需要一个解释性的weakly-equality-comparable-with概念。正如您所猜测的那样,这需要==有效。
修复方法是一行代码:
bool operator==(X const& other) const = default;

那么,既然<=>似乎已经可以独立完成任务,为什么还需要==呢?我只能猜测,但这可能是因为概念比我们仅仅需要operator<的情况更“完整”。如果一个类型可以使用<=>进行比较,它实际上也应该支持相等性。

至于为什么<=>不能在没有默认值的情况下覆盖==,这是因为对于可以短路判断相等性的类(如向量和字符串)以及包含此类类型的任何类来说,这会导致性能陷阱。没有任何迹象表明相等性比较是逐个元素进行的而不是短路的,所以<=>不处理相等性,除非它可以保证你避免了这个陷阱(通过默认<=>)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接