Пожалуйста, рассмотрите следующий код:
protocol P {}
class X {}
class Y: P {}
func foo<T>(_ closure: (T) -> Void) { print(type(of: closure)) }
func foo<T>(_ closure: (T) -> Void) where T: P { print(type(of: closure)) }
let xClosure: (X?) -> Void = { _ in }
foo(xClosure) // prints "(Optional<X>) -> ()"
let yClosure: (Y?) -> Void = { _ in }
foo(yClosure) // prints "(Y) -> ()"
Почему вызов foo(yClosure)
разрешается в версию foo
, ограниченную T: P
? Я понимаю, почему эта версия печатает то, что печатает, но я не понимаю, почему она вызывается вместо другой.
Мне кажется, что версия без P лучше подойдет для T == (Y?) -> Void
. Конечно, ограниченная версия более специфична, но требует преобразования (неявное преобразование из (Y?) -> Void
в (Y) -> Void
), в то время как не-P версия может быть вызвана без преобразования.
Есть ли способ исправить этот код таким образом, чтобы P-ограниченная версия вызывалась только в том случае, если тип параметра переданного замыкания напрямую соответствует P без каких-либо неявных преобразований?
foo
, я думаю, что потеря необязательности может быть объяснена.Y?
не соответствуетP
, ноY
соответствует, так что эту версиюfoo
можно вызвать, только выполнивT == Y
(в отличие отT == Y?
) и преобразовав замыкание из(Y?) -> Void
в(Y) -> Void
. Это преобразование допустимо и может происходить неявно (let cl: (Y) -> Void = yClosure
тоже работает, а также forums.swift.org/t/implicit-promotion-of-Optional/12381/4). Но я все еще не понимаю, почему эта перегрузкаfoo
выбирается в первую очередь. - person imre   schedule 11.06.2020closure: (T?) -> Void
работает, но не идеально, потому что в реальном коде, где я столкнулся с этим, замыкание имеет несколько параметров, любой из них может быть необязательным, поэтому это приведет к комбинаторному взрыву. Но в этом простом случае это работает. - person imre   schedule 11.06.2020