【问题标题】:inconsistent rules for operator overloading in swiftswift中运算符重载的规则不一致
【发布时间】:2017-12-27 05:20:07
【问题描述】:

我发现 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 编译器会以不同的方式处理这些情况吗?

【问题讨论】:

  • 还有没有办法在上级算子中强制调用Float > Float

标签: swift recursion operator-overloading


【解决方案1】:

不同的是,标准库目前在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
  }

  // [...]
}

(在运行 gyb.py 时,${Self} 将被 FloatDoubleFloat80 替换)

当涉及到重载决议时,+(Float, Float) -&gt; Float 重载将在与 Float 操作数一起应用时胜过 (Value, Value) -&gt; Float 重载,因为后者需要将它们从 Float 转换为 @ 987654337@.

但是Float 没有直接定义了&gt; 重载; 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)
}

当涉及到重载解决方案时,您的(Value, Value) -&gt; Bool 重载将战胜这个通用重载,因为重载解决方案有利于非通用重载(请参阅切线in my answer here)。因此,您会反复出现。您可以通过将 &gt; 的重载设置为泛型而不是 &lt;T : Value&gt;(T, T) -&gt; Bool 来解决此问题,但随后它将不再适用于异构的 Value 操作数。

假设您希望运算符适用于异构操作数,一个万无一失的解决方案是通过使用嵌套的 FloatNumericComparable 的一致性将调用调度到 &gt;+通用函数。这不能递归,因为
(Value, Value) -&gt; Float(Value, Value) -&gt; Bool 都不能满足这些各自协议的 +&gt; 要求。

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())
}

(请注意,我们为+&gt; 这样做,因为+ (Float, Float) -&gt; Float 重载可能会被删除以支持通用重载,这将导致与&gt; 相同的问题)

另一种解决方案是完全避免分派到&gt;+,而只是在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) -&gt; Void 实现它的重载,则调用+= 可能会出现问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    相关资源
    最近更新 更多