正如本 Q&A 中所提到的,作为static
成员实现的运算符重载与作为顶级函数实现的运算符重载之间存在差异。static
成员采用额外的(隐式)self
参数,编译器需要能够推断出该参数。
那么self
推断的价值如何呢?好吧,它必须从重载的操作数或返回类型中完成。对于协议扩展,这意味着其中一种类型需要是Self
. 请记住,您不能直接调用类型的运算符(即您不能说(Self.<)(a, b)
)。
考虑以下示例:
protocol Value {
func get() -> Float
}
extension Value {
static func < (a: Value, b: Value) -> Bool {
print("Being called on conforming type: \(self)")
return a.get() < b.get()
}
}
struct S : Value {
func get() -> Float { return 0 }
}
let value: Value = S()
print(value < value) // Ambiguous reference to member '<'
self
调用 to的价值是什么<
?编译器无法推断它(我真的认为它应该直接在重载时出错,因为它是不可调用的)。请记住,self
协议扩展中的静态范围必须是具体的符合类型;它不能只是Value.self
(因为协议扩展中的静态方法只能调用具体的符合类型,而不是协议类型本身)。
我们可以通过将重载定义为顶级函数来修复上述示例和您的示例:
protocol Value {
func get() -> Float
}
func < (a: Value, b: Value) -> Bool {
return a.get() < b.get()
}
struct S : Value {
func get() -> Float { return 0 }
}
let value: Value = S()
print(value < value) // false
这是可行的,因为现在我们不需要推断self
.
我们还可以self
通过使一个或两个参数采用来为编译器提供一种推断 的值的方法Self
:
protocol Value {
func get() -> Float
}
extension Value {
static func < (a: Self, b: Self) -> Bool {
print("Being called on conforming type: \(self)")
return a.get() < b.get()
}
}
struct S : Value {
func get() -> Float { return 0 }
}
let s = S()
print(s < s)
// Being called on conforming type: S
// false
编译器现在可以self
从操作数的静态类型进行推断。但是,如上所述,这需要是一个具体的类型,因此您不能处理异构 Value
操作数(您可以使用一个操作数获取Value
; 但不能同时使用两者,因为这样就无法推断self
)。
尽管请注意,如果您提供 的默认实现<
,您可能还应该提供==
. 除非您有充分的理由不这样做,否则我还建议您使这些重载采用同质的具体操作数(即 type 的参数Self
),以便它们可以为Comparable
.
另外,我建议使用可设置的属性要求,而不是拥有get()
和要求:set(to:)
// Not deriving from Comparable could be useful if you need to use the protocol as
// an actual type; however note that you won't be able to access Comparable stuff,
// such as the auto >, <=, >= overloads from a protocol extension.
protocol Value {
var floatValue: Double { get set }
}
extension Value {
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.floatValue == rhs.floatValue
}
static func < (lhs: Self, rhs: Self) -> Bool {
return lhs.floatValue < rhs.floatValue
}
}
最后,如果Comparable
一致性对于一致性至关重要Value
,则应使其派生自Comparable
:
protocol Value : Comparable {
var floatValue: Double { get set }
}
无论哪种情况,您都不需要min(of:and:)
函数,因为当符合类型符合时Comparable
,它可以使用顶级min(_:_:)
函数。