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).可以编写便利函数来解决这个问题,但这会产生巨大的代码爆炸.
>这将导致Tensor类成为单片类.我真的更喜欢它包含所需的最少量代码并通过辅助功能扩展其功能.
>与(2)相关,这意味着任何想要添加新功能的人都必须触及基类,我认为它设计不好.
编辑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"总结
以上是内存溢出为你收集整理的从间接调用在Swift 3中调用错误的专用泛型函数全部内容,希望文章能够帮你解决从间接调用在Swift 3中调用错误的专用泛型函数所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)