imr*_*mre 5 generics overload-resolution swift
对于通用自由函数,我可以使用重载,从本质上将函数专门化为函数类型,如下所示:
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"
Run Code Online (Sandbox Code Playgroud)
注意第二个版本foo()是不受协议约束的,主要是因为据我所知,我们不能让函数类型符合协议(我们不能扩展非名义类型)。我可以创建一个OneParamFunction协议,并且可以在一个受约束的 中使用它foo(),但是我不能让所有的单参数函数类型都符合那个协议。
但是上述重载在没有协议约束的情况下工作。
对于泛型类的实例方法,这样的事情可能吗?
对我来说,这种语法似乎是最自然的,但它不受支持:
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") }
}
Run Code Online (Sandbox Code Playgroud)
在 Generic 类上创建协议约束扩展的“正常”方式如下所示:
extension Generic1 where T: OneParamFunction { ... }
Run Code Online (Sandbox Code Playgroud)
但如上所述,我无法使函数类型符合 OneParamFunction 协议。
我也不能只创建一个(无重载/特化)实例方法然后转发到自由函数,这不起作用:
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"
Run Code Online (Sandbox Code Playgroud)
编译,但总是调用未知-T 版本,我认为是因为类型擦除。在 Generic2 中,编译器并不真正知道 T 是什么。Generic2 没有在 T 上定义任何协议约束来帮助编译器正确分派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"
Run Code Online (Sandbox Code Playgroud)
这里在调用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"
Run Code Online (Sandbox Code Playgroud)
我还有其他选择吗?
更新,以回应 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"
Run Code Online (Sandbox Code Playgroud)
这确实调用了 的“T 是函数”版本foo,即使T在内部g()也进行了类型擦除。而且我认为这Generic(type(of: f)).foo()比 Rob 的g<T>()调用示例更相似 foo()(这更类似于Generic.foo()来自其他成员的调用Generic——在这种情况下,我确实理解为什么T是未知的)。
在这两种情况下(Generic(type(of: f)).foo()vs foo(g(type(of: f))))都有两种类型:
f,以及Generic.init()/ g())。但显然,foo()在调用 free 函数时foo(),随后的调用是根据类型 #1 分派的,而类型 #2 用于分派到成员函数Generic.foo()。
首先,我认为差异与上面示例中的g()返回方式有关T.Type,而结果Generic.init()是 a 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
Run Code Online (Sandbox Code Playgroud)
在这种情况下,Generic.init和 都g()返回Generic<T>。然而,free_foo()呼叫似乎是根据 的完整原始类型调度的f,而member_foo()呼叫则没有。我仍然想知道为什么。
您可能希望为您的 Generic 类使用多个泛型参数。
class Generic1<P, R> {
init(_ t: ((P) -> R).Type) {}
}
extension Generic1 where P == Void
{ func foo() { print("T is unknown") } }
extension Generic1{
func foo() { print("T is a function with one parameter") }
}
let f: (String) -> Void = { print($0) }
Generic1(type(of: f)).foo() // prints "T is a function with one parameter"
let v: (()) -> Void = { print($0) } // a bit ugly ;)
Generic1(type(of: v)).foo() // prints "T is unknown"
Run Code Online (Sandbox Code Playgroud)
但使用泛型类型别名会更好;)
因此,考虑到您的评论,我尝试:
()s这是我得到的:
// some generic type aliases
typealias Bar<P, R> = (P) -> R
typealias Foo<P> = Bar<P, Void>
typealias Quux<P, Q, R> = (P, Q) -> R
typealias Qux<P, Q> = Quux<P, Q, Void>
typealias Xyzyy<S, P, Q, R> = (S, P, Q) -> R
// some closures
let fooString: Foo<String> = { print($0) }
let barIntVoid: Bar<Int, Void> = { print($0) }
let quuxStringIntString: Quux<String, Int, String> = { "\($0)\($1)"}
let quuxStringIntVoid: Quux<String, Int, Void> = { print("\($0)\($1)") }
let xyzyyDateStringIntVoid: Xyzyy<Date, String, Int, Void> = { print("\($0): \($1)\($2)") }
// same class as before
class Generic2<G> {
init(_ t: G.Type) {}
}
// handling any type
extension Generic2 {
func foo<T>(_ f: T) {
print("\(T.self) is \(T.self == G.self ? "known" : "unknown")")
}
}
// these methods are put in an unspecialized extension in order to be "shared"
// I guess if your designing a module you probably won't be able to handle all the possibilities
// but I'm not sure you should anyway.
// it should be possible to extends Generic2 outside it's module to handle custom case though
extension Generic2 {
func foo<P,R>(p: P.Type, r: R.Type) {
print("f is a function with one parameter of type `\(P.self)` returning `\(R.self)`")
print("\(Bar<P,R>.self) is \(G.self == Bar<P,R>.self ? "known" : "unknown")")
}
func foo<P, Q,R>(p: P.Type, q: Q.Type, r: R.Type) {
print("f is a function with two parameter of type `\(P.self)` and `\(Q.self)` returning `\(R.self)`")
print("\(Quux<P, Q, R>.self) is \(G.self == Quux<P, Q, R>.self ? "known" : "unknown")")
}
func foo<S, P, Q,R>(s: S.Type, p: P.Type, q: Q.Type, r: R.Type) {
print("f is a function with two parameter of type `\(S.self)`, `\(P.self)` and `\(Q.self)` returning `\(R.self)`")
print("\(Xyzyy<S, P, Q, R>.self) is \(G.self == Xyzyy<S, P, Q, R>.self ? "known" : "unknown")")
}
}
// you have to create an extension an write an overload of `foo(_:)` for each type you want to support
extension Generic2 where G == Bar<String, Void> {
func foo(_ f: G) {
foo(p: String.self, r: Void.self)
}
}
extension Generic2 where G == Bar<Int, Void> {
func foo(_ f: G) {
foo(p: Int.self, r: Void.self)
}
}
extension Generic2 where G == Quux<String, Int, String> {
func foo(_ f: G) {
foo(p: String.self, q: Int.self, r: String.self)
}
func foo(p: String, q: Int, f: G) {
foo(f)
f(p,q)
}
}
extension Generic2 where G == Quux<String, Int, Void> {
func foo(_ f: G) {
foo(p: String.self, q: Int.self, r: Void.self)
}
func foo(p: String, q: Int, f: G) {
foo(f)
f(p,q)
}
}
Run Code Online (Sandbox Code Playgroud)
我这样测试过:
print("fooString:")
Generic2(Foo<String>.self).foo(fooString)
print("\nbarIntVoid:")
Generic2(Bar<Int, Void>.self).foo(barIntVoid)
print("\nquuxStringIntString:")
Generic2(Quux<String, Int, String>.self).foo(quuxStringIntString)
print("\nquuxStringIntString:")
Generic2(Quux<String, Int, Void>.self).foo(quuxStringIntString)
print("\nquuxStringIntVoid:")
Generic2(Quux<String, Int, Void>.self).foo(p: "#", q:1, f: quuxStringIntVoid) // prints "#1"
print("\nxyzyyDateStringIntVoid:")
Generic2(Xyzyy<Date, String, Int, Void>.self).foo(xyzyyDateStringIntVoid)
print("\nnon function types:")
Generic2(Foo<String>.self).foo(Int.self)
Generic2(Foo<String>.self).foo(1)
Generic2(Int.self).foo(1)
Run Code Online (Sandbox Code Playgroud)
输出如下所示:
fooString:
f is a function with one parameter of type `String` returning `()`
(String) -> () is known
barIntVoid:
f is a function with one parameter of type `Int` returning `()`
(Int) -> () is known
quuxStringIntString:
f is a function with two parameter of type `String` and `Int` returning `String`
(String, Int) -> String is known
quuxStringIntString:
(String, Int) -> String is unknown
quuxStringIntVoid:
f is a function with two parameter of type `String` and `Int` returning `()`
(String, Int) -> () is known
#1
xyzyyDateStringIntVoid:
(Date, String, Int) -> () is known
non function types:
Int.Type is unknown
Int is unknown
Int is known
Run Code Online (Sandbox Code Playgroud)
此时我不确定是否应该保留以前的编辑,但这个更短。
我刚刚将第二个重载更改为:
class Generic_<T> {
init(_ t: T.Type) {}
func member_foo() { print("T is unknown") }
func member_foo<P>(_ type: P.Type) { print("T is a function with one parameter") }
}
Run Code Online (Sandbox Code Playgroud)
free_function 的行为没有改变:
free_foo(g_(type(of: f))) // T is function
free_foo(g_(String.self)) // T is unknown
Run Code Online (Sandbox Code Playgroud)
但现在它也适用于Generic_的成员:
let generic = Generic_(Bar<String, Int>.self)
generic.member_foo() // T is unknown
generic.member_foo(String.self) // T is a function with one parameter
Run Code Online (Sandbox Code Playgroud)