swift中运算符重载的不一致规则

时间:2017-12-27 05:20:08

标签: swift recursion operator-overloading

我发现swift超载+>重叠的方式存在一些奇怪的差异:

protocol Value {
    func get() -> Float
    mutating func set(to:Float)
}
extension Float : Value {
    func get() -> Float {
        return self
    }
    mutating func set(to value:Float) {
        self = value
    }
}
func + (a:Value, b:Value) -> Float {
    return a.get() + b.get()
}
func > (a:Value, b:Value) -> Bool {
    return a.get() > b.get()
}

let a:Float = 1
let b:Float = 2
let c:Float = a + b //this works fine. Compiler calls Float + Float
let d = b > a //this causes infinite loop. Compiler recursively calls Value > Value

任何想法为什么swift编译器以不同的方式处理这些情况?

1 个答案:

答案 0 :(得分:0)

区别在于标准库目前在+上有Float重载defined directly

//  TODO: These should not be necessary, since they're already provided by
//  <T: FloatingPoint>, but in practice they are currently needed to
//  disambiguate overloads.  We should find a way to remove them, either by
//  tweaking the overload resolution rules, or by removing the other
//  definitions in the standard lib, or both.

extension ${Self} {
  @_inlineable // FIXME(sil-serialize-all)
  @_transparent
  public static func + (lhs: ${Self}, rhs: ${Self}) -> ${Self} {
    var lhs = lhs
    lhs += rhs
    return lhs
  }

  // [...]
}

(其中${Self}将由FloatDouble&amp; Float80替换为gyb.py运行)

当涉及到重载解析时,(Float, Float) -> Float的{​​{1}}重载会在应用+个操作数时超过(Value, Value) -> Float重载,因为后者需要它们从Float转换为Float

但是Value 不会直接在其上定义Float重载; it's instead defined as a top-level generic function超过>个操作数:

FloatingPoint

当涉及到重载解析时,您的@_transparent public func > <T : FloatingPoint>(lhs: T, rhs: T) -> Bool { return rhs.isLess(than: lhs) } 重载将胜过此泛型重载,因为重载解析有利于非泛型重载(请参阅正切in my answer here)。因此,你最终会反复出现。您可以通过使(Value, Value) -> Bool泛型超过>来解决此问题,但之后它将不再适用于异构<T : Value>(T, T) -> Bool操作数。

假设您希望运算符适用于异构操作数,一个万无一失的解决方案是通过Value符合>来调用+Float的调用。 {1}}和Numeric使用嵌套的通用函数。这不能解决问题因为,Comparable(Value, Value) -> Float都不能满足这些协议的(Value, Value) -> Bool+要求。

>

(请注意,我们为func + (lhs: Value, rhs: Value) -> Float { func add<T : Numeric>(_ lhs: T, _ rhs: T) -> T { return lhs + rhs } return add(lhs.get(), rhs.get()) } func > (lhs: Value, rhs: Value) -> Bool { func greaterThan<T : Comparable>(_ lhs: T, _ rhs: T) -> Bool { return lhs > rhs } return greaterThan(lhs.get(), rhs.get()) } 以及+执行了此操作,因为可能会删除>重载以支持通用重载,这会导致相同与+ (Float, Float) -> Float

一样的问题

另一种解决方案是避免完全转发>>,而只需调用+Float运算符上的isLess(than:)方法,模仿stdlib实现:

+=

如果为func + (lhs: Value, rhs: Value) -> Float { var lhsFloat = lhs.get() lhsFloat += rhs.get() return lhsFloat } func > (lhs: Value, rhs: Value) -> Bool { return rhs.get().isLess(than: lhs.get()) } 实现了+=的重载,则调用(inout Value, Value) -> Void可能会有问题。