如何在Swift中调用不明确的泛型函数?

时间:2014-09-29 20:29:53

标签: generics swift ambiguity

我已经定义了两个通用函数

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)
当然,斯威夫特抱怨并抛出错误 模糊地使用&#39; 这是可以理解的,因为我不清楚是否要使用可比较 Hashable Int 符合这两者)

有没有办法可以提示我想要使用哪个编译器?

1 个答案:

答案 0 :(得分:11)

这是不明确的,因为Int同时是HashableComparable,并且这两个协议都不在同一层次结构中。 (您可以查看Int protocol 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的运算方式,否则对于同时实现ComparableForwardIndexType的类型将不明确。


为了进一步扩展,这里看看我的意思是“由于每个协议的相关类型要求,你无法明确告诉它要调用哪个函数。”协议可以用作类型,如Swift书籍chapter on Protocols

中所述
protocol RandomNumberGenerator {
    func random() -> Double
}

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

在此示例中,generator属性可以是符合RandomNumberGenerator的任何类型 - 类似于Objective-C中id<ProtocolName>的使用方式。但是,如果协议在声明中不包含关联类型或引用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'

相反,我们需要使用t关键字将(upcast?)as转换为特定协议,并以这种方式调用特定的泛型函数:

p(t as Printable)
// Printable: true

p(t as BooleanType)
// BooleanType: true

因此,只要我们有一个合格的协议,我们就可以选择要调用的泛型方法的哪个版本。