从间接调用在Swift 3中调用错误的专用泛型函数
我的代码遵循以下一般设计:
protocol DispatchType {} class DispatchType1: DispatchType {} class DispatchType2: DispatchType {} func doBar<D:DispatchType>(value:D) { print("general function called") } func doBar(value:DispatchType1) { print("DispatchType1 called") } func doBar(value:DispatchType2) { print("DispatchType2 called") } 实际上DispatchType实际上是一个后端存储. doBarfunctions是依赖于正确存储类型的优化方法.如果我这样做,一切正常: let d1 = DispatchType1() let d2 = DispatchType2() doBar(value: d1) // "DispatchType1 called" doBar(value: d2) // "DispatchType2 called" 但是,如果我创建一个调用doBar的函数: func test<D:DispatchType>(value:D) { doBar(value: value) } 我尝试了类似的呼叫模式,我得到: test(value: d1) // "general function called" test(value: d2) // "general function called" 这似乎是Swift应该能够处理的东西,因为它应该能够在编译时确定类型约束.就像快速测试一样,我也尝试过将doBar写成: func doBar<D:DispatchType>(value:D) where D:DispatchType1 { print("DispatchType1 called") } func doBar<D:DispatchType>(value:D) where D:DispatchType2 { print("DispatchType2 called") } 但得到相同的结果. 任何想法,如果这是正确的Swift行为,如果是这样,一个很好的方法来绕过这种行为? 编辑1:我试图避免使用协议的原因示例.假设我有代码(从我的实际代码中大大简化): protocol Storage { // ... } class Tensor<S:Storage> { // ... } 对于Tensor类,我有一组可以在Tensors上执行的基本操作.但是,操作本身将根据存储更改其行为.目前我完成了这个: func dot<S:Storage>(_ lhs:Tensor<S>,_ rhs:Tensor<S>) -> Tensor<S> { ... } 虽然我可以将它们放在Tensor类中并使用扩展: extension Tensor where S:CBlasStorage { func dot(_ tensor:Tensor<S>) -> Tensor<S> { // ... } } 这有一些我不喜欢的副作用: >我认为dot(lhs,rhs)优于lhs.dot(rhs).可以编写便利函数来解决这个问题,但这会产生巨大的代码爆炸. 编辑2:一种替代方案是,如果您对所有内容使用约束,那么事情就会起作用 func test<D:DispatchType>(value:D) where D:DispatchType1 { doBar(value: value) } func test<D:DispatchType>(value:D) where D:DispatchType2 { doBar(value: value) } 将导致调用正确的doBar.这也不是理想的,因为它会导致编写大量额外的代码,但至少让我保留当前的设计. 编辑3:我遇到的文档显示了静态关键字与泛型的使用.这至少有点(1): class Tensor<S:Storage> { // ... static func cos(_ tensor:Tensor<S>) -> Tensor<S> { // ... } } 允许你写: let result = Tensor.cos(value) 它支持运算符重载: let result = value1 + value2 它确实具有所需Tensor的额外冗长.这可以通过以下方式改善: typealias T<S:Storage> = Tensor<S> 解决方法
这确实是正确的行为,因为重载解析在编译时发生(在运行时发生这将是非常昂贵的操作).因此,在test(value :)中,编译器唯一知道的值是它符合DispatchType的某种类型 – 因此它可以调度到的唯一重载是func doBar< D:DispatchType>(值:D).
如果泛型函数总是由编译器专门化,那么情况会有所不同,因为那时test(value :)的专门实现会知道具体的值类型,因此能够选择适当的重载.但是,泛型函数的专业化目前只是一种优化(因为没有内联,它可能会给代码增加大量膨胀),因此这不会改变观察到的行为. 允许多态性的一种解决方案是通过添加doBar()作为协议要求来利用协议见证表(参见this great WWDC talk),并在符合协议的各个类中实现它的专用实现,一般实现是协议扩展的一部分. 这将允许动态调度doBar(),从而允许从test(value :)调用它并调用正确的实现. protocol DispatchType { func doBar() } extension DispatchType { func doBar() { print("general function called") } } class DispatchType1: DispatchType { func doBar() { print("DispatchType1 called") } } class DispatchType2: DispatchType { func doBar() { print("DispatchType2 called") } } func test<D : DispatchType>(value: D) { value.doBar() } let d1 = DispatchType1() let d2 = DispatchType2() test(value: d1) // "DispatchType1 called" test(value: d2) // "DispatchType2 called" (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- flash – AS3使用PrintJob打印MovieClip
- postgresql – Postgres应用程序尝试使用不正确的套接字文件
- vue v-model实现自定义样式多选与单选功能
- sqli-labs(23)
- OK6410裸机简单的NANDFLASH读写及擦
- objective-c – 如何将NSString转换为iPhone sdk中的UTF-8格
- Oracle在英文匹配时大小写敏感,如何忽略大小写进行匹配
- 安装Ruby on Mountain Lion的问题 – ruby?? 1.9.3 wont’c
- React/React Native 的ES5 ES6写法对照表
- dwr 服务器推送 反应不及时 nginx