对于通用的自由函数,我可以使用重载,
本质上专门化函数类型的函数,如下所示:
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 是函数”版本。 , 即使 T
在 g()
内被类型删除也。我认为这更类似于 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.init
和 g()
返回 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/