web-dev-qa-db-fra.com

Impossible de spécialiser explicitement une fonction générique

J'ai un problème avec le code suivant:

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

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

le generic1 (name) résultat du message d'erreur du compilateur "Impossible de spécialiser explicitement une fonction générique"

Est-il possible d'éviter cette erreur? Je ne peux pas changer la signature de la fonction generic1, elle devrait donc être (String) -> Void

78
Greyisf

J'ai également eu ce problème et j'ai trouvé une solution de contournement pour mon cas.

Dans cet article l'auteur a le même problème

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

Le problème semble donc être que le compilateur doit en quelque sorte déduire le type de T. Mais il n'est pas permis d'utiliser simplement <type> générique (params ...).

Normalement, le compilateur peut rechercher le type de T, en analysant les types de paramètres, car c’est là que T est utilisé dans de nombreux cas.

Dans mon cas, c'était un peu différent, parce que le type de retour de ma fonction était T. Dans votre cas, il semble que vous n'ayez pas utilisé T du tout dans votre fonction. Je suppose que vous venez de simplifier l'exemple de code.

J'ai donc la fonction suivante

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

Et en cas de, par exemple

getProperty<Int>("countProperty")

le compilateur me donne la

Cannot explicitly specialize a generic function

Ainsi, pour donner au compilateur une autre source d’informations permettant de déduire le type de T, vous devez déclarer explicitement le type de la variable dans laquelle la valeur de retour est enregistrée.

var value : Int = getProperty("countProperty")

De cette façon, le compilateur sait que T doit être un entier.

Donc, globalement, cela signifie simplement que si vous spécifiez une fonction générique, vous devez au moins utiliser T dans vos types de paramètre ou comme type de retour.

136
ThottChief

Swift 4.2

Il existe généralement plusieurs façons de définir des fonctions génériques. Mais ils sont basés sur la condition que T doit être utilisé en tant que parameter, ou en tant que return 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
    }
}

Après cela, nous devons fournir T lors de l'appel.

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

Ref:

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

La solution prend le type de classe en paramètre (comme en Java)

Pour que le compilateur sache de quel type il s’agit passe la classe comme argument

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

Appeler comme:

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

Vous n'avez pas besoin d'un générique ici, car vous avez des types statiques (String en paramètre), mais si vous souhaitez qu'une fonction générique en appelle une autre, vous pouvez procéder comme suit.

Utiliser des méthodes génériques

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
} 

Utilisation d'une classe générique (moins flexible car le générique peut être défini pour un seul type par instance)

class Foo<T> {

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

   func doOtherStuff(text: String) -> T {

   }  

}

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

Je pense que lorsque vous spécifiez une fonction générique, vous devez spécifier certains des paramètres de type T, comme suit:

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

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

et si vous voulez appeler la méthode handle (), vous pouvez le faire en écrivant un protocole et en spécifiant une contrainte de type pour 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)
}

vous pouvez donc appeler cette fonction générique avec String:

generic2("Some")

et il va compiler

2
Valerii Lider

J'ai eu un problème similaire avec ma fonction de classe générique class func retrieveByKey<T: GrandLite>(key: String) -> T?.

Je ne pouvais pas l'appeler let a = retrieveByKey<Categories>(key: "abc") où Catégories est une sous-classe de GrandLite.

let a = Categories.retrieveByKey(key:"abc") a renvoyé GrandLite, pas des catégories. Les fonctions génériques ne déduisent pas le type en fonction de la classe qui les appelle.

class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T? m'a donné une erreur lorsque j'ai essayé let a = Categories.retrieveByKey(aType: Categories, key: "abc") m'a dit qu'il ne pouvait pas convertir Categories.Type en GrandLite, même si Categories est une sous-classe de GrandLite. TOUTEFOIS...

class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?did fonctionne si j'ai essayé let a = Categories.retrieveByKey(aType: [Categories](), key: "abc") apparemment une affectation explicite d'une sous-classe ne fonctionne pas, mais une affectation implicite utilisant un autre type générique (tableau) fonctionne dans Swift 3.

0
adamek