Swift:为函数类型专门化泛型类的方法

标签 swift generics overload-resolution

对于通用的自由函数,我可以使用重载,
本质上专门化函数类型的函数,如下所示:

func foo<T>(_ t: T.Type) { print("T is unknown") }
func foo<P>(_ t: ((P) -> Void).Type) { print("T is a function with one parameter") }

let f: (String) -> Void = { print($0) }    
foo(type(of: f))   //  prints "T is a function with one parameter"

注意 foo() 的第二个版本不受协议(protocol)约束,
主要是因为据我所知,我们不能让函数类型符合协议(protocol)
(我们不能扩展非名义类型)。我可以创建一个 OneParamFunction协议(protocol),
并且可以在受限的 foo() 中使用它,但我不能让所有的单一参数
函数类型符合该协议(protocol)。

但是上述重载在没有协议(protocol)约束的情况下工作。

对于泛型类的实例方法,这样的事情可能吗?

对我来说,这种语法似乎是最自然的,但它不受支持:
class Generic1<T> { init(_ t: T.Type) {} }
extension Generic1 { func foo() { print("T is unknown") } }

extension Generic1<P>
    where T == ((P) -> Void) {
    func foo() { print("T is a function with one parameter") }
}

在 Generic 类上创建协议(protocol)约束扩展的“正常”方式
看起来像这样:
extension Generic1 where T: OneParamFunction { ... }

但如上所述,我不能使函数类型符合 OneParamFunction 协议(protocol)。

我也不能只创建一个(没有重载/特化)实例方法
然后转发到免费功能,这不起作用:
class Generic2<T> {
    init(_ t: T.Type) {}
    func foo() { myModule.foo(T.self) }
}

let f: (String) -> Void = { print($0) }
Generic2(type(of: f)).foo()   //  prints "unknown T"

编译,但总是调用 unknown-T 版本,我认为是因为类型删除。
在 Generic2 中,编译器并不真正知道 T 是什么。
Generic2 没有在 T 上定义任何有助于编译器的协议(protocol)约束
正确发送myModule.foo()调用(它不能有这样的限制,见上文)。

在泛型类中使用方法重载可以编译并且看起来很接近,
但仍然不起作用,尽管在这种情况下我不知道为什么。
class Generic3<T> {
    init(_ t: T.Type) {}
    func foo() { print("T is unknown") }
    func foo<P>() where T == ((P) -> Void) { print("T is a function with one parameter") }
}

let f: (String) -> Void = { print($0) }
Generic3(type(of: f)).foo()   //  prints "unknown T"

在此调用 foo() 的网站Generic3 的类型参数是完全已知的,
所以在我看来,编译器将拥有所有必要的类型信息
正确调度调用,但事实并非如此,它仍然打印“未知 T”。

甚至没有将类型作为参数重复到 foo()帮助(无论如何都不理想):
class Generic4<T> {
    init(_ t: T.Type) {}
    func foo(_ t: T.Type) { print("T is unknown") }
    func foo<P>(_ t: T.Type) where T == ((P) -> Void) { print("T is a function with one parameter") }
}

let f: (String) -> Void = { print($0) }
Generic4(type(of: f)).foo(type(of: f))   //  still prints "unknown T"

我还有其他选择吗?

更新 ,以回应 Rob Napier 的回答。

我认为我在这里希望的不是真正的动态调度,我想要静态调度,而是基于调用站点已知的所有类型信息,而不是基于 T 的类型删除值之前在 Generic.init() 期间推断.这确实适用于自由函数,但不适用于成员函数。

试试这个:
func foo<T>(_ t: T.Type) { print("T is unknown") }
func foo<P>(_ t: ((P) -> Void).Type) { print("T is a function with one parameter") }

func g<T>(_ x: T.Type) -> T.Type { return x }
let f: (String) -> Void = { print($0) }
foo(g(type(of: f)))   //  prints "T is a function"

这确实调用了 foo 的“T 是函数”版本。 , 即使 Tg() 内被类型删除也。我认为这更类似于 Generic(type(of: f)).foo()比 Rob 的示例 g<T>()调用foo() (这更类似于从 Generic.foo() 的其他成员调用 Generic ——在这种情况下,我明白为什么 T 是未知的)。

在这两种情况下( Generic(type(of: f)).foo()foo(g(type(of: f))) )有两种类型:
  • f 的原始类型, 和
  • 第一次调用返回的类型( Generic.init()/g() )。

  • 但显然随后调用 foo()在调用自由函数 foo() 时根据类型 #1 调度, 而类型 #2 用于分派(dispatch)到成员函数 Generic.foo() .

    首先,我认为差异与上述示例中的方式有​​关g()返回 T.Type ,而 Generic.init() 的结果是 Generic<T> , 但不是:
    class Generic_<T> {
        init(_ t: T.Type) {}
        func member_foo() { print("T is unknown") }
        func member_foo<P>() where T == ((P) -> Void) { print("T is a function with one parameter") }
    }
    
    func free_foo<T>(_ g: Generic_<T>) { print("T is unknown") }
    func free_foo<P>(_ t: Generic_<(P) -> Void>) { print("T is a function with one parameter") }
    
    func g_<T>(_ t: T.Type) -> Generic_<T> { return Generic_(t) }
    
    free_foo(g_(type(of: f)))   //  T is function
    Generic_(type(of: f)).member_foo()   //  T is unknown
    

    在这种情况下,Generic.initg()返回 Generic<T> .然而,free_foo()调用似乎是根据 f 的完整原始类型发送的,而 member_foo()调用没有。我仍然想知道为什么。

    最佳答案

    是的,有点,但是你正在做的事情并没有真正按照你想要的方式工作,并且其他解决方案将以类似的方式失败,基本上使它无用。

    首先,让我们跳到您正在寻找的答案(但不会做您可能想要的)。你的问题只是语法。 Swift 不支持这种语法:

    extension Generic1<P>
        where T == ((P) -> Void) {
        func foo() { print("T is a function with one parameter") }
    }
    

    相反,你这样写:
    extension Generic1
    {
        func foo<P>() where T == ((P) -> Void) { print("T is a function with one parameter") }
    }
    

    正如我所说,这只是语法。这没什么深奥的,Swift 可能会在以后改进它。但是你想要做的是深刻的, splinter 的。以这种方式重载不会使静态事物动态化。像这样的特化绝对不能改变语义,因为你不能确定哪个会被调用。例如,使用您的顶级函数:
    func g<T>(_ x: T) {
        foo(type(of: x))
    }
    
    g(1) // T is unknown
    g(f) // T is unknown
    

    问题是 g解决 foo在“T 可以是任何类型”的上下文中。在这种情况下,它会选择您的“未知”案例。这是在编译时根据可用的最佳信息确定的。如果编译器可以证明 T(P) -> Void ,那么它会选择另一个重载,但是这里不能证明。最糟糕的是,如果编译器将来改进,它可能会调用另一个函数。

    像这样的模棱两可的重载的重点是优化,而不是替代基于类的继承。例如,某些算法在任何序列上都是可能的,但在双向集合上更有效,因此对 where Self: BidirectionalCollection 进行重载是有意义的。在可能的情况下使事情变得更快,但在任何一种情况下结果都必须相同。

    所以回到我原来的答案,它匹配你的代码,但它不会做你想要的:
    let x = Generic1(type(of: f))
    x.foo() // T is unknown
    

    关于Swift:为函数类型专门化泛型类的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62355933/

    相关文章:

    swift - 检查核心数据中是否存在记录

    swift - Realm 正在重写而不是更新嵌套的对象数组

    ios - 设备人脸识别 - Swift

    c# - 如何为表单控件创建模板函数?

    Java 泛型 : Bound mismatch. 寻找有效的替代品

    c++ - 这应该编译吗?重载分辨率和隐式转换

    c++ - 为什么缩小不影响重载分辨率?

    iOS 11 屏幕截图标记

    c# - 检查 T 是否实现接口(interface)并在填充接口(interface)属性后返回 T

    c++ - int 与 std::vector<int> 的重载分辨率与单个 int 的初始化列表