【问题标题】:Swift Generic Type InferenceSwift 泛型类型推断
【发布时间】:2017-02-25 13:12:49
【问题描述】:

所以我想知道是否有人可以解释这个错误背后的原因或解释我做错了什么。

我正在尝试创建一个通用函数,该函数采用协议约束类型,该类型有一个名为solve 的静态方法。

但由于某种原因,即使它在 Xcode 中很好地解决了约束,编译器也会发出嘶嘶声。

是否有任何原因它不能推断出我已经指定的类型,或者我的代码示例中是否存在幼稚错误?

编辑:因为它不够明确

我知道如何解决它,我只是想知道为什么接口/协议中的静态成员有问题。

protocol NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node]
}

func findPath<T: NodeSolver>(nodes: [Node]) -> [Node]  {        
        return T.solve(nodes)
    }

【问题讨论】:

  • 您可能会喜欢这个令人难以置信的 QA .. stackoverflow.com/questions/37240091
  • @luk2302 我将类型指定为 NodeSolver(参见:
  • @luk2302 那么这个类必须是静态的吗?我不明白为什么编译器无法推断类型。我在一个类型上调用静态成员。它只需要类型信息。我在泛型类型参数中交给它
  • @SacredGeometry 我删除了我的 cmets,我没有看到您对静态方法的引用。
  • @luk2303 哈哈,现在你明白我的困惑了吧?

标签: swift generics types protocols inference


【解决方案1】:

因为您似乎希望findPath 成为一种与符合NodeSolver类型 密切相关的方法,但不会在@987654324 中使用此具体NodeSolver 类型的任何实例@ 方法本身,您可能需要考虑简单地将通用 findPath 方法添加为默认实现可用于符合 NodeSolver 的所有类型的类型方法。

例如:

struct Node {}

protocol NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node]
    static func findPath(nodes: [Node]) -> [Node]
}

extension NodeSolver {
    static func findPath(nodes: [Node]) -> [Node]  {
        // hopefully some more logic ...
        return Self.solve(nodes)
    }
}

struct MyNodeSolver: NodeSolver {
    // dummy solver
    static func solve(_ nodes: [Node]) -> [Node] {
        return nodes
    }
}

let myNodes = [Node(), Node()]

// make use of the default implementation of `findPath` available
// to all types conforming to 'NodeSolver': this method itself
// make use of the concrete type-specific implementation of 'solve'
// in the types conforming to 'NodeSolver'.
let dummyPath = MyNodeSolver.findPath(nodes: myNodes)

我在我指定的协议中处理了一个受约束的类型。和 方法调用中的实际类型。

findPath<NodeSolver1>(nodes)
findPath<NodeSolver2>(nodes)

另一个可能更接近您想要实现的解决方法是将泛型函数包装成一个泛型类型(例如,struct),它包含一个非泛型函数findPath w.r.t。泛型类型的具体版本。如果从拥有类型外部的角度查看包装的 findPath,则函数是通用的 w.r.t。所属类型的泛型类型持有者。

例如:

struct Node {}

protocol NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node]
}

struct NodeSolverA: NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node] {
        return nodes
    }
}

struct NodeSolverB: NodeSolver {
    static func solve(_ nodes: [Node]) -> [Node] {
        return nodes.reversed()
    }
}

// instead of a generic function, wrap a concrete function
// in a generic type
struct AnyNodeSolver<T: NodeSolver> {
    static func findPath(nodes: [Node]) -> [Node]  {
        return T.solve(nodes)
    }
}

let myNodes = [Node(), Node()]

let dummyPathA = AnyNodeSolver<NodeSolverA>.findPath(nodes: myNodes)
let dummyPathB = AnyNodeSolver<NodeSolverB>.findPath(nodes: myNodes)

【讨论】:

  • 顺便说一句,我知道如何解决它,我只是想知道为什么接口/协议中的静态成员有问题。我将编辑问题以包含此评论,因为我认为该位不够明确。
  • @SacredGeometry 啊,编辑后我意识到这根本不能回答你的问题。尽管如此,如果读者对一种可能的解决方法感兴趣(其他答案显示了将泛型类型作为附加参数包含在泛型函数的声明中的解决方法),我会留下它。
  • @SacredGeometry 我用另一个你可能没有想到的“解决方法”更新了答案:这是处理findPath 的一种方法,就好像它是一个通用函数一样,但没有明确需要包含函数参数列表中的泛型类型(因为泛型类型实际上由拥有实际非泛型函数findPath 的泛型包装器拥有)。
  • 感谢您的宝贵时间。同样,它更多的是理论上的好奇心而不是实现问题。我只是好奇编译器如何处理泛型。我意识到,如果您将协议作为泛型类型参数传递,则无法辨别它使用什么实现。但是......例如:假设,一个类约束存在于swift中。编译器需要哪些其他信息来区分传递给它的类型,然后是泛型中的类型参数
【解决方案2】:

您必须在函数签名中指定T 的类型。

func findPath<T: NodeSolver>(nodes: [Node], ofType type: T.Type) -> [Node]  {        
    return T.solve(nodes)
}

【讨论】:

  • 我想 :) 这毕竟是错误的状态。我要问的是。那么泛型的意义何在?如果必须指定两次?
  • 只是以防它不清楚(因为它发生过一次)。该函数是静态的。编译器不需要实例,只需要解析它的类型。
  • 这就是为什么您不传递实例而是传递类型的原因。你必须告诉编译器使用哪种类型
  • 好的,我会换个角度试试。 swift中有类限制吗? msdn.microsoft.com/en-us/library/d5x73970.aspx 如果是这样,那么它必须是一个实现而不是协议,所以它需要是我传递给它的内容
【解决方案3】:

如果你定义了一个泛型类型,你必须让编译器以某种方式推断出实际的类型。目前没有办法实现这一目标。您将如何使用不同的NodeSolvers 对该方法进行两次调用?

Yannick 写了一个指定类型的答案 - 你问“那么泛型的意义何在?如果你必须指定它两次?” - 你不必。一般示例:

protocol P {
    static func solve(argument : String)
}

class P1 : P {
    class func solve(argument : String) {
        print("first")
    }
}

class P2 : P {
    class func solve(argument : String) {
        print("second")
    }
}


func doSome(argument : String, a : P.Type) {
    a.solve(argument: argument)
}

doSome(argument: "", a: P1.self) // prints "first"
doSome(argument: "", a: P2.self) // prints "second"

在你的情况下:

func findPath(nodes: [Node], solver: NodeSolver.Type) -> [Node]  {        
    return solver.solve(nodes)
}

【讨论】:

  • 我正在处理我指定的协议中的约束类型。以及方法调用中的实际类型。通常语言会限制接口中的静态成员,我很高兴看到 Swift 没有,但后来它不允许这样做,我只是好奇编译器在推断我正在约束的类型并在编译时交给它的技术障碍是什么.无论如何,我知道很多解决这个问题的方法,(我只是将它传递给一个代表)我只是想弄清楚这里真正的难题是什么。
  • @SacredGeometry 请展示如何使用两种不同的NodeSolver 实现来调用your 方法findPath。根本没有办法——不可能有任何推断,编译器绝对没有任何信息可以推断出任何东西。请显示更多上下文。
  • findPath(nodes); findPath(节点);
  • 都是实现 NodeSolver 协议/接口的类
  • @SacredGeometry 调用方法时不允许显式指定类型约束。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多