1

我正在尝试实现一个基本的协议扩展,如下所示:

protocol Value {
    func get() -> Float
    mutating func set(to:Float)
}
extension Value {
    static func min(of a:Value, and b:Value) -> Float {
        if a < b { //Expression type 'Bool' is ambiguous without more context
            return a.get()
        }else{
            return b.get()
        }
    }
    static func < (a:Value, b:Value) -> Bool {
        return a.get() < b.get()
    }
}

在该if子句中,编译器说:Expression type 'Bool' is ambiguous without more context. 为什么这不起作用?

4

3 回答 3

1

正如本 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(_:_:)函数。

于 2017-12-21T21:19:16.183 回答
1

你不能写

if a < b {

因为a并且b有类型Value不是Comparable

但是,您可以比较floatab

if a.get() < b.get() {
于 2017-12-21T19:33:31.373 回答
0

如果您希望能够创建可以使用 , , 等运算符的类型><它们==必须符合Comparable协议:

 protocol Value: Comparable {
    func get() -> Float
    mutating func set(to: Float)
}

不过,这有更多限制。您必须将Value协议扩展中的所有类型更改为Self

extension Value {
    static func min(of a: Self, and b: Self) -> Float {
        if a < b { //Expression type 'Bool' is ambiguous without more context
            return a.get()
        }else{
            return b.get()
        }
    }

    static func < (a: Self, b: Self) -> Bool {
        return a.get() < b.get()
    }
}

Self类型被替换为实现协议的类型。因此,如果我Value在 type 上实现Container,方法签名将如下所示:

class Container: Value {
    static func min(of a: Container, and b: Container) -> Float

    static func < (a: Container, b: Container) -> Bool
}

作为旁注,如果您想Value符合Comparable,您可能还需要将==运算符添加到Value扩展中:

static func <(lhs: Self, rhs: Self) -> Bool {
    return lhs.get() < rhs.get()
}
于 2017-12-21T21:14:03.547 回答