当前位置: 首页 > 面试题库 >

在Swift 3中从间接调用中调用了错误的专用泛型函数

孟楷
2023-03-14
问题内容

我有遵循以下常规设计的代码

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其实是一个后端存储。这些doBar功能是优化方法,取决于正确的存储类型。如果我这样做,一切正常:

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该类,我有一组基本操作,可以在上执行Tensor。但是,操作本身将根据存储更改其行为。目前,我通过以下方式完成此任务:

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> {
       // ...
    }
}

这有一些我不喜欢的副作用:

  1. 我认为dot(lhs, rhs)是更可取的lhs.dot(rhs)。可以编写便利功能来解决此问题,但这会产生大量的代码。

  2. 这将导致Tensor该类变得单一。我真的更喜欢让它包含所需的最少代码,并通过辅助功能扩展其功能。

  3. 与(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 :我看到文档显示了static泛型使用关键字。这至少有助于点(1):

class Tensor<S:Storage> {
   // ...
   static func cos(_ tensor:Tensor<S>) -> Tensor<S> {
       // ...
   }
}

允许您编写:

let result = Tensor.cos(value)

它支持运算符重载:

let result = value1 + value2

它确实具有required的附加冗长性Tensor。使用以下方法可以使效果更好:

typealias T<S:Storage> = Tensor<S>

问题答案:

这确实是正确的行为,因为重载解析是在编译时进行的(在运行时进行这将是非常昂贵的操作)。因此,从内部看test(value:),编译器唯一了解的value就是它符合某种类型DispatchType-因此它可以分派给它的
唯一 重载是func doBar<D : DispatchType>(value: D)

如果泛型函数始终由编译器进行特殊处理,则情况将有所不同,因为这样的特殊实现test(value:)将知道的具体类型,value从而能够选择适当的重载。但是,泛型函数的专业化目前仅是一种优化(因为没有内联,它会给您的代码带来很大的膨胀),因此这不会改变观察到的行为。

为了实现多态性,一种解决方案是通过添加协议见证者表(请参阅有关WWDC的精彩演讲),方法是添加doBar()为协议需求,并在符合协议的各个类中实现它的专门实现,通用实现是协议扩展的一部分。

这将允许动态分配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"


 类似资料:
  • 问题内容: 有些事情像 但是,这是 在这种情况下,“ 1”似乎没有多大意义,以下工作正常: 您能否指向ECMAScript的描述该语法的特定部分? 问题答案: 此运算符仅从左至右求值其操作数,并从第二个运算符返回值,例如: 在调用函数的上下文中,对操作数的求值将仅获取一个值,而不是引用,这将导致被调用函数内部的值指向全局对象(或者它将处于新的ECMAScript5 Strict模式下) 。 例如:

  • 问题内容: 如果我有一个像这样的抽象类: 还有一些从Item派生的类是这样的: 我不明白为什么我不能使用泛型调用构造函数: 我知道可以有一个没有构造函数的类型,但是这种情况是不可能的,因为Pencil具有没有参数的构造函数,而Item是抽象的。但是我从eclipse中得到了这个错误: 无法实例化 我不明白为什么的 T类型 ,以及如何避免这种情况? 问题答案: 无法使用Java类型系统来强制类层次结

  • 问题内容: 我正在使用jQuery的网页上工作。我有一个Ajax调用,它从服务器获取数据并更新div。在该数据内部有一个jQuery函数,但是在将数据加载到页面中之后未调用该函数。我已经在页面中包含了正确的js文件。 这是从Ajax调用返回并放入div的内容: 将html插入页面后,如何使返回的javascript运行? (我将Rails与jRails插件结合使用) 问题答案: 如果要对具有htm

  • 我一直在尝试泛型,很快我就遇到了一些我无法解释的事情 例如: 我不明白

  • 我有一个Foo和Bar对象的列表,以及每个相应对象的转换器。 Convert-method需要有所不同,因为Bar1与Bar2和Bar3等有很大不同,但是我想创建一个方法来处理所有可能的列表。 是否可以创建一个泛型方法,根据列表的内容调用相应的非泛型方法? 到目前为止,我已经尝试过了: 但这并不能编译,因为"无法解析方法'Converts(T, S)'" 有什么想法吗?

  • 我是C++的新手,我写了一个小程序来了解赋值如何处理对象。这个页面(http://www.cplusplus.com/doc/tutorial/classes2/)的cpp文档提示我这样做。在这一页上,它指出: 隐式版本[复制赋值操作符]执行浅层复制,这适用于许多类,但不适用于具有指向对象的指针的类,这些对象处理其存储。在这种情况下,不仅类会冒两次删除指向对象的风险,而且赋值会通过在赋值之前不删除