矛盾的协议一致性

Contradictory protocol conformances

谁能解释一下,这是怎么回事:

struct Test {
    var value: Int
}

// -----------------------------------
protocol Test1: Equatable {
    var value: Int { get set }
}
extension Test1 {
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.value == rhs.value + 1
    }
}
// -----------------------------------
protocol Test2: Equatable {
    var value: Int { get set }
}
extension Test2 {
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.value == rhs.value + 2
    }
}
// -----------------------------------

extension Test: Test1 {}
extension Test: Test2 {}

let a = Test(value: 5)
let b = Test(value: 5)

print(a == b) // true, which is unexpected

如果仅符合 Test1 或仅符合 Test2 它会按预期工作。
符合 Test1Test2。起初我认为顺序很重要。但看起来只是互相抵消了!没有任何警告。这是非常违反直觉的。

注意: Test 符合 Equatable 而不是 因为你提供的两个扩展,但是因为auto-generated Equatable 实现。正如您所说,如果只有这两个扩展名,那么 ==Equatable 实现将是模棱两可的。

Test1Test2 协议扩展中的 == 均未被调用。相反,调用 auto-generated Equatable 实现。您可能还记得,== 运算符对于属性全部为 Equatable 的类型来说是 auto-generated,并且被声明为符合 Equatable 本身。

这是因为在扩展中声明的成员使用静态绑定,所以当同一个成员有多个版本可用时,只有在编译时类型是扩展的类型时才会选择在扩展中声明的版本。例如:

protocol P { }
class C : P { func f() { print("C") } }
extension P { func f() { print("P") } }
C().f() // C
(C() as P).f() // P

a == b中,ab都是Test,所以选择了none个扩展运算符。但是,由于 Test1Test2 都使用 Self,您只能将它们用作通用约束,而不能转换为它们。因此,我认为您根本无法调用扩展中声明的 ==

无论如何,如果您想看到一条错误消息,指出有重复的 == 运算符可用:

struct Test {
    var value: Int
    var x: Any? // now there is no auto-generated Equatable implementation!
}

error: type 'Test' does not conform to protocol 'Equatable' extension Test: Test1 {} ^

note: candidate exactly matches

   static func == (lhs: Self, rhs: Self) -> Bool {
               ^

note: candidate exactly matches

   static func == (lhs: Self, rhs: Self) -> Bool {
               ^

如果您删除其中一个扩展名,那么 Test 由于扩展名 符合 Equatable ,因为不再有歧义。因此,auto-generated 实现不再是 auto-generated,只有一个 == 可供选择 - 扩展中声明的那个。