swift 无法显式专用化泛型函数

13z8s7eq  于 2023-02-21  发布在  Swift
关注(0)|答案(8)|浏览(136)

我有以下代码的问题:

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

func generic2<T>(name : String){
     generic1<T>(name)
}
  • generic 1(name)* 导致编译器错误“无法显式专用化泛型函数”

有什么方法可以避免这个错误吗?我不能改变generic 1函数的签名,因此它应该是(String)-〉Void

pengsaosao

pengsaosao1#

这里不需要泛型,因为这里有静态类型(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")
x8diyxa7

x8diyxa72#

我认为当你指定泛型函数时,你应该指定一些类型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")

它会编译

vdzxcuhz

vdzxcuhz3#

到目前为止,我个人的最佳实践是使用@orkhan-alikhanov的答案。今天,当我研究SwiftUI以及.modifier()ViewModifier是如何实现的时,我发现了另一种方法(或者说它更像是一种变通方法?)
只需将第二个函数 Package 到struct中。

示例:

如果这个函数给你“不能显式专用化泛型函数”

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

下面这个可能会有帮助。将generic1的声明 Package 成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的解决方法是一个很好的例子。这里的解决方法没有更多的信息-但是通过了编译器。相同的信息,但是不同的结果?那么一定是出了问题。如果是编译器错误,它可以得到修复。
k97glaaz

k97glaaz4#

我的泛型类函数class func retrieveByKey<T: GrandLite>(key: String) -> T?也有类似的问题。
我不能称之为let a = retrieveByKey<Categories>(key: "abc"),因为类别是GrandLite的子类。
let a = Categories.retrieveByKey(key:"abc")返回了GrandLite,而不是Categories。泛型函数不会基于调用它们的类来推断类型。
class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?给了我一个错误,当我尝试let a = Categories.retrieveByKey(aType: Categories, key: "abc")给了我一个错误,它不能转换类别。类型到GrandLite,即使类别是GrandLite的子类。但是...
class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?did work如果我尝试let a = Categories.retrieveByKey(aType: [Categories](), key: "abc"),显然子类的显式赋值不起作用,但是使用另一个泛型类型(数组)的隐式赋值在Swift 3中起作用。

gcxthw6b

gcxthw6b5#

func generic1<T>(of type: T.Type, name: String) {
    switch type {
    case is String.Type:
        print("String generic")
    case is Int.Type:
        print("Int generic")
    default:
        print("\(type) generic")
    }
    print(name)
}

func generic2<T>(of type: T.Type, name: String) {
     generic1(of: T.self, name: name)
}

generic2(of: String.self, name: "hello")
// String generic
// hello
generic2(of: Int.self, name: "world")
// Int generic
// world
generic2(of: Float.self, name: "bye")
// Float generic
// bye

由于在Swift中以generic<T>()的身份调用是非法的,
这种generic(of: T.self)用法是我找到的一种变通方法。

9avjhtql

9avjhtql6#

我也有这个问题,我找到了一个变通办法,我的情况。
在这篇文章中,作者也有同样的问题
https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide
所以问题似乎是,编译器需要以某种方式推断T的类型,但不允许简单地使用泛型(params ...)。< type >(params...).
通常情况下,编译器可以通过扫描参数类型来查找T的类型,因为在许多情况下,这是使用T的地方。
在我的例子中,有点不同,因为我的函数的返回类型是T,而在你的例子中,你似乎根本没有在函数中使用T,我猜你只是简化了示例代码。
所以我有以下函数

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

如果,比如说

getProperty<Int>("countProperty")

编译器给出了错误:
无法显式专用化泛型函数
因此,为了给编译器提供另一个信息源来推断T的类型,必须显式声明保存返回值的变量的类型。

var value : Int = getProperty("countProperty")

这样,编译器就知道T必须是整数。
所以我认为总的来说,它只是意味着,如果你指定了一个泛型函数,你至少要在参数类型中使用T,或者作为返回类型。

djp7away

djp7away7#

雨燕5号

通常有很多方法来定义泛型函数,但是它们都基于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
yws3nbqq

yws3nbqq8#

解决方案是将类类型作为参数(类似于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
    })

相关问题