首页 > 解决方案 > 为什么泛型类型在调用链的下游不通过约束参与方法重载?

问题描述

Swift 允许通过传入的泛型类型上的约束来重载泛型方法。如果将其与具体类型一起使用,则传入的类型将参与此重载,并且将从该类型推断出约束。

一旦泛型方法通过重载解析委托给另一个泛型方法,就不能再推断约束,而是利用上面已经放置在类型上的约束。

protocol Conformance {}

extension String : Conformance {}

// #1
func baseMethod<T>(_ value: T) {
    let isConforming = T.self is Conformance.Type
}

// #2
func baseMethod<T>(_ value: T) where T : Conformance {
    let isConforming = T.self is Conformance.Type
}

func delegatingMethod<T>(_ value: T) {
    baseMethod(value)
}

func run() {
    // Calls #2, isConforming = true
    baseMethod(String())
    // Calls #1, isConforming = true
    delegatingMethod(String())
}

我假设这是存在的,因此无论在何处使用泛型类型,您都可以从调用站点获得足够的类型信息,了解哪些约束适用,但它似乎严重且人为地限制了通过约束重载的效用。

是否有任何已知的解决方法来解决这个奇怪的问题?模仿这一点的东西将非常有用。

标签: swiftgenericsconstraintsoverloading

解决方案


Swift 允许通过传入的泛型类型上的约束来重载泛型方法。

是的......但要非常清楚,这是一个静态重载,而不是动态覆盖。它基于可以在编译时证明的类型。

func delegatingMethod<T>(_ value: T) {
    baseMethod(value)
}

我们现在正在编译它,我们需要将它作为一个具体的静态函数调用(可能是内联的)写入二进制文件。我们知道什么T?我们对此一无所知T,因此任何where子句都会失败。

我们甚至不知道这个函数是如何被调用的,因为调用可能来自另一个编译单元或模块。虽然原则上,它可能具有基于访问级别的不同语义,例如当它是私有的并且所有调用都可以评估时使用一个版本,而当它是公共的时使用另一个版本,这将是一个非常可怕的错误来源。

您所要求的是将delegatingMethod其关于要进行什么函数调用的决定推迟到运行时。这不是泛型的工作方式。此外,您要求将所有where子句编码在二进制文件的某个位置,以便可以在运行时对其进行评估。也不是泛型如何工作。这将需要一个比 Swift 想要实现的更动态的调度系统。这并非不可能;它只是一种完全不同的动物,并且阻止了很多优化。

这感觉就像您正在尝试使用协议和泛型重新发明类继承。你不能。它们是不同的解决方案,具有不同的功能。类继承基本上是动态的。协议和泛型基本上是静态的。如果您想要基于特定类型的动态调度,请使用类。


推荐阅读