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

Why does std::tuple call operator <=> twice?

以下代码调用运算符 <=> 两次,参数颠倒。但是为什么?

GCC 10.2 和 clang 12 似乎都在使用 libstdc++-10,它的 确实提供了运算符 <=>,所以它似乎不是缺少标准库支持的情况,我的代码必须是不正确的。如何解决?

#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}};
}

简答:您需要为 X.

定义 operator==

std::tuple 通过综合三向比较来比较元素,只有当类型满足 std::three_way_comparable_with<T,U> 时才使用 <=>。最终,这需要 std::three_way_comparable<X>,这需要一个说明性的 weakly-equality-comparable-with 概念。您可能会猜到,这需要 == 才有效。

修复是一行:

bool operator==(X const& other) const = default;

既然 <=> 似乎可以独立完成这项工作,为什么还需要 ==?我只能推测,但这可能是由于概念比我们习惯的更“完整”,例如只需要 operator<。如果一个类型可以与 <=> 进行比较,那么它实际上也应该支持相等性。

至于为什么<=>不覆盖==除非默认,这是因为类的性能缺陷,其等式可以短路(例如向量和字符串)以及任何包含此类类型的 类。没有迹象表明相等比较每个元素而不是短路,所以 <=> 不处理相等,除非它可以保证你会避免这个陷阱(通过默认 <=>)。