无法明确专门化泛型函数

时间:2015-01-15 14:17:38

标签: generics swift

我遇到以下代码问题:

func generic1<T>(name : String){
}

func generic2<T>(name : String){
     generic1<T>(name)
}

generic1(name)导致编译错误“无法明确专门化泛型函数”

有什么办法可以避免这个错误吗?我无法更改generic1函数的签名,因此它应该是(String) - &gt;空隙

7 个答案:

答案 0 :(得分:129)

我也遇到了这个问题,我找到了一个针对我案例的解决方法。

在本文中,作者有同样的问题

https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide

所以问题似乎是,编译器需要以某种方式推断出T的类型。但是,不允许简单地使用泛型&lt; type&gt;(params ...)。

通常,编译器可以通过扫描参数类型来查找T的类型,因为这是在许多情况下使用T的地方。

在我的情况下它有点不同,因为我的函数的返回类型是T.在你的情况下,似乎你还没有在你的函数中使用过T。我猜你刚刚简化了示例代码。

所以我有以下功能

func getProperty<T>( propertyID : String ) -> T

如果是,例如

getProperty<Int>("countProperty")

编译器给了我

Cannot explicitly specialize a generic function

因此,为了给编译器提供另一个信息来源来推断T的类型,你必须显式地声明保存返回值的变量的类型。

var value : Int = getProperty("countProperty")

这样编译器知道T必须是整数。

所以我认为总的来说它只是意味着如果你指定一个泛型函数,你必须至少在参数类型中使用T或作为返回类型。

答案 1 :(得分:23)

Swift 4.2

通常,有许多方法可以定义泛型函数。但它们基于T必须用作parameterreturn type的条件。

extension UIViewController {
    class func doSomething<T: UIView>() -> T {
        return T()
    }

    class func doSomethingElse<T: UIView>(value: T) {
        // Note: value is a instance of T
    }

    class func doLastThing<T: UIView>(value: T.Type) {
        // Note: value is a MetaType of T
    }
}

之后,我们必须在致电时提供T

let result = UIViewController.doSomething() as UIImageView // Define `T` by casting, as UIImageView
let result: UILabel = UIViewController.doSomething() // Define `T` with property type, as UILabel
UIViewController.doSomethingElse(value: UIButton()) // Define `T` with parameter type, as UIButton
UIViewController.doLastThing(value: UITextView.self) // Define `T` with parameter type, as UITextView

参考:

  1. http://austinzheng.com/2015/01/02/swift-generics-pt-1/
  2. https://dispatchswift.com/type-constraints-for-generics-in-swift-d6bf2f0dbbb2

答案 2 :(得分:16)

解决方案是将类类型作为参数(如在Java中)

让编译器知道他正在处理的类型将类作为参数传递

extension UIViewController {
    func navigate<ControllerType: UIViewController>(_ dump: ControllerType.Type, id: String, before: ((ControllerType) -> Void)?){
        let controller = self.storyboard?.instantiateViewController(withIdentifier: id) as! ControllerType
        before?(controller)
        self.navigationController?.pushViewController(controller, animated: true)
    }
}

请致电:

self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: {
        controller in
        controller.user = self.notification.sender
    })

答案 3 :(得分:3)

你不需要通用的,因为你有静态类型(String作为参数),但是如果你想要一个泛型函数调用另一个,你可以做以下。

使用通用方法

func fetchObjectOrCreate<T: NSManagedObject>(type: T.Type) -> T {
    if let existing = fetchExisting(type) {
       return existing
    }
    else {
        return createNew(type)
    }
}

func fetchExisting<T: NSManagedObject>(type: T.Type) -> T {
    let entityName = NSStringFromClass(type)
     // Run query for entiry
} 

func createNew<T: NSManagedObject>(type: T.Type) -> T {
     let entityName = NSStringFromClass(type)
     // create entity with name
} 

使用泛型类(不太灵活,因为通用只能为每个实例定义1种类型)

class Foo<T> {

   func doStuff(text: String) -> T {
      return doOtherStuff(text)
   }

   func doOtherStuff(text: String) -> T {

   }  

}

let foo = Foo<Int>()
foo.doStuff("text")

答案 4 :(得分:2)

我认为当你指定泛型函数时,你应该指定一些T类型的参数,如下所示:

func generic1<T>(parameter: T) {
    println("OK")
}

func generic2<T>(parameter: T) {
    generic1(parameter)
}

如果你想调用handle()方法,那么你可以通过编写协议,并为T指定类型约束来实现这一点:

protocol Example {
    func handle() -> String
}

extension String: Example {
    func handle() -> String {
        return "OK"
    }
}

func generic1<T: Example>(parameter: T) {
    println(parameter.handle())
}

func generic2<T: Example>(parameter: T) {
    generic1(parameter)
}

所以你可以用String调用这个泛型函数:

generic2("Some")

它将编译

答案 5 :(得分:1)

到目前为止,我个人的最佳做法是使用@ orkhan-alikhanov的答案。如今,在查看SwiftUI以及.modifier()ViewModifier的实现方式时,我发现了另一种方式(或更有效的解决方法?)

只需将第二个函数包装到struct中。

示例:

如果这给您“无法显式专门化泛型函数”

func generic2<T>(name: String){
     generic1<T>(name)
}

这可能会有所帮助。将generic1的声明包装到struct中:

struct Generic1Struct<T> {
    func generic1(name: String) {## do, whatever it needs with T ##}
}

并调用:

func generic2<T>(name : String){
     Generic1Struct<T>().generic1(name: name)
}

备注:

  • 在出现此错误消息时,我不知道它是否有任何帮助。我只知道,这件事发生时我被困了很多次。我确实知道,当出现错误消息时,该解决方案在今天很有帮助。
  • Swift处理泛型的方式对我来说仍然令人困惑。
  • 该示例以及使用struct的变通办法是一个很好的示例。这里的解决方法没有更多信息-但可以通过编译器。信息相同,但结果不同?然后出了点问题。如果它是编译器错误,则可以修复。

答案 6 :(得分:0)

我的泛型类函数class func retrieveByKey<T: GrandLite>(key: String) -> T?遇到了类似的问题。

我无法将其称为let a = retrieveByKey<Categories>(key: "abc"),其中Categories是GrandLite的子类。

let a = Categories.retrieveByKey(key:"abc")返回了GrandLite,而不是分类。通用函数不会根据调用它的类来推断类型。

当我尝试class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?给我一个错误,它无法将Categories.Type转换为GrandLite时,

let a = Categories.retrieveByKey(aType: Categories, key: "abc")给了我一个错误,即使Categories是GrandLite的子类。无论其...

class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T? 做了工作如果我尝试let a = Categories.retrieveByKey(aType: [Categories](), key: "abc")显然一个子类的显式赋值不起作用,但使用另一个泛型类型(数组)的隐式赋值确实有效斯威夫特3。