web-dev-qa-db-ja.com

ジェネリック関数を明示的に特化することはできません

次のコードに問題があります:

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

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

generic1(name)コンパイラエラーの結果「明示的にジェネリック関数を特殊化できません」

このエラーを回避する方法はありますか? generic1関数のシグネチャを変更できないため、(String)-> Voidにする必要があります

78
Greyisf

私もこの問題を抱えており、私のケースの回避策を見つけました。

この記事では、著者にも同じ問題があります

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

だから問題は、コンパイラが何らかの形でTの型を推測する必要があるようです。しかし、単にgeneric <type>(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を使用する必要があることを意味すると思います。

136
ThottChief

Swift 4.2

通常、汎用関数を定義するには多くの方法があります。しかし、それらはTparameterとして、または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
    }
}

その後、呼び出し時に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
31
nahung89

解決策は、クラス型をパラメーターとして使用することです(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
    })
17

静的な型(パラメーターとしての文字列)があるため、ここでジェネリックは必要ありませんが、ジェネリック関数を別の関数で呼び出したい場合は、次のようにすることができます。

ジェネリックメソッドの使用

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
aryaxt

ジェネリック関数を指定するときは、次のような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")

コンパイルされます

2
Valerii Lider

汎用クラス関数class func retrieveByKey<T: GrandLite>(key: String) -> T?でも同様の問題がありました。

CategoriesがGrandLiteのサブクラスであるlet a = retrieveByKey<Categories>(key: "abc")と呼ぶことはできませんでした。

let a = Categories.retrieveByKey(key:"abc")は、カテゴリではなくGrandLiteを返しました。ジェネリック関数は、それらを呼び出すクラスに基づいて型を推測しません。

CategoriesがGrandLiteのサブクラスであるにもかかわらず、class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?を試してlet a = Categories.retrieveByKey(aType: Categories, key: "abc")でCategories.TypeをGrandLiteに変換できないというエラーが表示されました。しかしながら...

class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?did私がlet a = Categories.retrieveByKey(aType: [Categories](), key: "abc")を試みた場合に機能する明らかにサブクラスの明示的な割り当ては機能しませんが、別のジェネリック型(配列)を使用した暗黙の割り当ては機能しますin Swift 3。

0
adamek