2017-02-25 57 views
0

所以我想知道是否有人能解释这个错误背后的理由或解释我做错了什么。Swift通用类型推理

我想创建一个通用函数,它需要一个协议约束类型,它有一个静态方法称为求解。

但由于某些原因,即使它解决了Xcode中的约束问题,编译器仍然会引发一些棘手的问题。

是否有任何理由不应该能够推断出我已经指定的类型,或者在我的代码示例中是否存在天真的错误?

编辑:由于它不是足够

我知道如何解决它明确的,我只是好奇,对于为什么在接口的静态成员/协议是有问题的解释。

enter image description here

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

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

您可以享受这个令人难以置信的QA .. http://stackoverflow.com/questions/37240091 – Fattie

+0

@ luk2302我指定类型NodeSolver(参见:) – SacredGeometry

+0

@ luk2302所以这个答案是类必须是静态的? 我不明白为什么编译器wouldnt能够推断类型。 我正在调用一个类型的静态成员。所有它应该需要的是类型信息。我正在交给它的泛型类型参数 – SacredGeometry

回答

1

因为它似乎想要findPath是强连接到的类型符合NodeSolver的方法,但在findPath方法本身不会使用这种混凝土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) 
+0

顺便说一句,我知道如何解决它,我只是很好奇解释为什么接口/协议中的静态成员有问题。我将编辑该问题以包含此评论,因为我不认为这一点足够明确。 – SacredGeometry

+0

@SacredGeometry啊,编辑后我意识到这并不能回答你的问题。我会离开它,没有少,如果读者有兴趣在一个可能的解决方法(在其他的答案显示解决方法包括通用型作为通用函数的声明。 – dfri

+0

@SacredGeometry一个额外的参数我更新的答案另一个“变通办法”,可能无法越过你的心灵:这是一个方法来处理'findPath'就好像它是一个通用的功能,但没有显式地需要包括在函数的参数列表(通用类型,因为泛型类型其实由拥有实际的非泛型函数'findPath'通用包装拥有)。 – dfri

1

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

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

我想通:)这传递给它那么Param类型的类型区分毕竟是什么样的错误状态。 我问的是。那么泛型的意义何在?如果你必须指定两次? – SacredGeometry

+0

只是说它没有清楚(因为它发生过一次)。 该功能是静态的。编译器不应该需要一个实例,只需要解决它的类型。 – SacredGeometry

+0

这就是为什么你没有通过一个实例,而是一个类型。你必须告诉编译器使用哪种类型 – Yannick

1

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

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) 
} 
+0

我我在我指定的协议中递交了一个consdtrained类型。方法调用中的实际类型。 通常语言限制接口中的静态成员我很高兴看到Swift没有做,但它不允许这样做,我只是好奇编译器的技术阻止者在推断我限制并交给它的类型编译时间。 无论如何,我知道很多解决这个问题的方法,(我将通过它代表) 我只是想弄清楚实际的难题在这里。 – SacredGeometry

+0

@SacredGeometry请展示如何使用两个不同的'NodeSolver'实现来调用* your *方法'findPath'。根本没有办法 - 不能有任何推论,编译器绝对没有信息可以推断任何事情。请多显示一下上下文。 – luk2302

+0

findPath (nodes); findPath (nodes); – SacredGeometry

0

谈过谁与这个回答一个同事(这是在C#中,但相关的)

,并解释来讲这个问题的问题语言实现。

感谢您的答案和时间。

https://blogs.msdn.microsoft.com/ericlippert/2007/06/14/calling-static-methods-on-type-parameters-is-illegal-part-one/

+1

实际上,在链接的文章中,** 3)**在Swift中是可能的:这是我在第二种方法(答案)中显示的情况,但是使用协议而不是“尝试静态多态性”。你的问题和文章的区别在于本文描述了一个泛型类,它在函数中使用泛型,而你的问题试图使用“免费泛型函数”。如果您考虑泛型类(或结构体)中的函数,则可以调用已构造的泛型类型的静态方法。到一些给定的协议或基类(POP;不是OOP) – dfri

+1

...为什么?因为我们使用协议作为_type restraint_,而不是类型化的类型(链接文章中的情况是多态的例子:'E'中的'T'可能是'C'或'D'),这意味着具体的'T'总是在Swift编译时知道的,只要我们确定遵循继承的构成。 – dfri

+0

所以你很抱歉,我一定忽略了这一点。我已经给你的回答设置了答案:) – SacredGeometry