【问题标题】:How to call ambiguous generic function in Swift?如何在 Swift 中调用模棱两可的泛型函数?
【发布时间】:2016-10-19 03:53:36
【问题描述】:

我定义了两个通用函数

func job<T: Comparable>(x: T) {
  println("1")
}

func job<T: Hashable>(x: T) {
  println("2")
}

当我尝试调用其中一个时,例如:

let myInt: Int = 1 // Explicit Int just for clarity of the example
job(myInt)

当然 Swift 会抱怨并抛出错误
“工作”的模糊使用
这是可以理解的,因为不清楚我是要使用 Comparable 还是 HashableInt 两者都符合)

有没有一种方法可以提示编译器我想使用哪个?

【问题讨论】:

  • job(myInt as Hashable) 工作吗?
  • 不:(我得到 2 个错误:Protocol 'Hashable' can only be used as a generic constraint because it has Self or assosiated type requirementsType 'Hashable' does not conform to protocol 'Comparable'(这听起来很奇怪 :))
  • 仅供参考,以获取最新的快速注释这两个很好的答案...stackoverflow.com/a/39836054/294884...stackoverflow.com/a/39835658/294884

标签: generics swift ambiguity


【解决方案1】:

这是模棱两可的,因为Int 既是Hashable 又是Comparable,而且这两个协议都不属于同一个层次结构。 (可以查看Intprotocol hierarchy on Swifter。)

func f<T: Hashable>(t: T) {
    println("Hashable: \(t)")
}
func f<T: Comparable>(t: T) {
    println("Comparable: \(t)")
}

let number = 5
f(number)
// error: ambiguous use of 'f'

由于每个协议的相关类型要求,您无法明确告诉它要调用哪个函数,但您可以做的是定义第三个函数:

func f<T: Comparable where T: Hashable>(t: T) {
    println("Both Hashable & Comparable: \(t)")
}
f(number)
// Both Hashable & Comparable: 5

这就是Swift implements ..&lt; 运算符的方式,否则对于同时实现ComparableForwardIndexType 的类型来说,这将是模棱两可的。


为了进一步扩展,这里看看我的意思是“你不能明确地告诉它调用哪个函数,因为每个协议都有相关的类型要求。”协议可以用作类型,如 Swift 书籍chapter on Protocols 中所述:

protocol RandomNumberGenerator {
    func random() -> Double
}

class Dice {
    let generator: RandomNumberGenerator
    // ...
}

在此示例中,生成器属性可以是符合 RandomNumberGenerator 的任何类型 - 类似于在 Objective-C 中使用 id&lt;ProtocolName&gt; 的方式。但是,协议只能在其声明中不包含关联类型或引用 Self 时用作类型。不幸的是,这几乎排除了 Swift 中的所有内置类型,包括 HashableComparable

Hashable继承自Equatable,在定义==运算符时引用Self

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

Comparable 对其运算符也是如此:

func <=(lhs: Self, rhs: Self) -> Bool
// similar definitions for <, >, and >=

这些协议只能用作通用约束,在声明变量时不能用作类型。 (据我所知,这是未记录的,但可以通过错误消息发现。)

两个没有限制的协议是PrintableBooleanType,所以我们可以看看它们是如何工作的。 Bool 是唯一符合BooleanType 的内置类型,它也是Printable,所以这将是我们的测试类型。这是我们的通用函数p() 和变量t - 请注意,和以前一样,我们不能只用t 调用函数:

func p<T: Printable>(t: T) {
    println("Printable: \(t)")
}
func p<T: BooleanType>(t: T) {
    println("BooleanType: \(t)")
}

let t: Bool = true
p(t)
// error: Ambiguous use of 'p'

相反,我们需要使用 as 关键字将t 强制转换(向上转换?)到特定协议,并以这种方式调用特定的通用函数:

p(t as Printable)
// Printable: true

p(t as BooleanType)
// BooleanType: true

所以只要我们有一个合格的协议,我们就可以选择调用泛型方法的哪个变体。

【讨论】:

  • 澄清一下,ComparableHashable 方法只是为了演示问题。我不希望需要它们,但我想了解我将来可能会受到多大的限制。现在,我不确定您所说的“您不能明确告诉它要调用哪个函数,因为每个协议的相关类型要求”。这里是否存在一些基本问题,能够为编译器提供有关使用哪种方法的提示?您认为我们将来可以期待这样的功能吗?
  • 不是告诉编译器使用哪种函数与泛型编程的设计相反(通常,不仅仅是在 Swift 中)?无论如何,如果您希望将来看到(或至少被告知不要期待)某个功能,最好发送至file a bug
  • @BartekChlebek:在这一点上添加了一点(好的,很多)解释。 Swift 肯定在不断变化,但我不一定认为它需要改变。这种冲突似乎比较少见,不是吗?
  • @NateCook 哇,感谢您的回答,现在很清楚了。虽然我同意这种冲突应该非常罕见,但我仍然认为这是一个可以在未来版本的 Swift 中解决的限制。您使用PrintableBooleanType 的示例表明其中一些功能已经存在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-24
  • 1970-01-01
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多