일반 함수를 명시 적으로 특수화 할 수 없습니다.


92

다음 코드에 문제가 있습니다.

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

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

generic1 (이름) 컴파일러 오류에 대한 결과는 "명시 적으로 일반적인 기능을 전문화 할 수 없습니다"

이 오류를 피할 수있는 방법이 있습니까? generic1 함수의 서명을 변경할 수 없으므로 (String)-> Void 여야합니다.


2
컨텍스트에 대해 유추 할 수없는 경우 여기에서 제네릭 유형을 사용하는 이유는 무엇입니까? 제네릭 유형이 내부적으로 만 사용되는 경우 함수 본문 내에서 유형을 지정해야합니다.
— Kirsteins

자리 표시 자 유형 T이 전혀 사용 generic1()됩니까? 컴파일러가 유형을 추론 할 수 있도록 해당 함수를 어떻게 호출 할까요?
— Martin R

3
나는 genetic1처럼 <blaClass> ( "SomeString") 함수를 호출 할 수있는 방법이 있기를 바랍니다
— Greyisf

2
제네릭은 컴파일러가 컨텍스트를 추론 할 수있는 경우에만 해당되지 않습니다. 기능을 명시 적으로 전문화하면 코드의 다른 부분이 유추 될 수 있습니다. 예 : 유형 func foo<T>() -> T { ... }의 객체 와 return . 명시 적으로 전문 수있는 것 에 infereed되어야한다 . 이런 식으로 함수를 호출하는 방법이 있었으면합니다. C #을을 수 있습니다tTtTt1var t1 = foo<T>()
— nacho4d

1
나는 이것도 만났다. 형식을 매개 변수로 전달해야하는 경우 제네릭 함수를 만드는 것은 의미가 없습니다. 이건 버그 일거야?!
— Nick

답변:


160

나는 또한이 문제가 있었고 내 사례에 대한 해결 방법을 찾았습니다.

이 기사에서 저자는 동일한 문제가 있습니다.

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

컴파일러는 나에게 오류를 제공합니다.

일반 함수를 명시 적으로 특수화 할 수 없습니다.

따라서 컴파일러에 T 유형을 추론 할 수있는 또 다른 정보 소스를 제공하려면 반환 값이 저장되는 변수의 유형을 명시 적으로 선언해야합니다.

var value : Int = getProperty("countProperty")

이렇게하면 컴파일러는 T가 정수 여야한다는 것을 알 수 있습니다.

그래서 전반적으로 제네릭 함수를 지정하면 최소한 T를 매개 변수 유형이나 반환 유형에 사용해야한다는 의미라고 생각합니다.


2
많은 시간을 절약했습니다. 감사합니다.
— chrislarson

4
반환 값이 없으면 그렇게 할 방법이 있습니까? 즉,func updateProperty<T>( propertyID : String )
— 카일 Bashour

1
왜 그렇게 하려는지 모르겠어요? 아무것도 반환하지 않기 때문에 함수를 제네릭으로 선언해도 이점이 없습니다.
— ThottChief 2016

@ThottChief에는 많은 이점이 있습니다. 예를 들어 키 경로를 사용 / 구축하거나 유형 이름을 문자열로 가져 오는 경우
— zaitsman

좋은 답변 감사합니다. 나는이 작업을 텐데 let value = foo() as? Type그것이에 사용될 수 있도록 if하거나 guard그 결과는 선택 사항이지만하지 않습니다 ...
— agirault

54

스위프트 5

일반적으로 일반 함수를 정의하는 방법에는 여러 가지가 있습니다. 그러나 , 또는으로 T사용되어야 하는 조건을 기반 parameter으로합니다 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

일반적인 문제에 대한 훌륭한 대답입니다 ... 해결 방법이 너무 많기 때문입니다. 또한 self.result = UIViewController.doSomething()선언 할 때 속성을 입력하는 한 자체적으로 작동 한다는 것을 깨달았 습니다.
— teradyl

많은 것을 설명하는 훌륭한 대답.
— Okhan Okbay

자바 doLastThing<UITextView>()는 Swift doLastThing(UITextView.self)가되는데 적어도 최악은 아닙니다. 복잡한 결과를 명시 적으로 입력하는 것보다 낫습니다. 해결 방법에 감사드립니다.
— Erhannis

18

솔루션은 클래스 유형을 매개 변수로 사용합니다 (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
    })

1
이것은 받아 들인 대답보다 위대하고 훨씬 낫습니다. 역사적인 부분을 제거하기 위해 편집을 고려하거나 적어도 솔루션 아래에 두십시오. 감사!
— Dan Rosenstark 2017

1
:) 피드백에 대한 @DanRosenstark 감사
— Orkhan Alikhanov

이것이 효과가 있지만 모범 사례라고 생각하지 않습니다. 그 이유는 이제 함수 정의가 캐스트에 문제가있는 경우 수행 할 작업을 결정하기 때문입니다. 따라서 여기서 캐스트가 실패하면 충돌이 발생합니다. 클라이언트에 따라 다를 수 있으므로 클라이언트가 수행 할 작업을 결정하도록하는 것이 좋습니다. 그래서 저는이 이유 때문에이 답변에 반대표를 던질 것입니다.
— smileBot

@smileBot 당신은 예제가 나쁘거나 접근 방식을 의미합니까?
— Orkhan Alikhanov

접근. 실용적인 경우 전문화를 연기하는 것이 프로그래밍의 일반적인 규칙이라고 생각합니다. 이것은 책임을 이해한다는 생각의 일부입니다. 제네릭을 전문화하는 것은 기능의 책임이 아닙니다. 함수가 모든 호출자에게 필요한 것이 무엇인지 알 수 없기 때문에 이것이 호출자의 책임입니다. 함수가이 역할을 맡으면 이득이없는 함수의 사용이 제한됩니다. 이것을 많은 코딩 문제로 일반화 할 수 있습니다. 이 단일 개념은 저에게 엄청난 도움이되었습니다.
— smileBot

4

정적 유형 (매개 변수로 문자열)이 있기 때문에 여기서 제네릭이 필요하지 않지만 제네릭 함수가 다른 함수를 호출하도록하려면 다음을 수행 할 수 있습니다.

일반 방법 사용

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
} 

제네릭 클래스 사용 (인스턴스 당 하나의 유형에 대해서만 제네릭을 정의 할 수 있으므로 유연성이 떨어짐)

class Foo<T> {

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

   func doOtherStuff(text: String) -> T {

   }  

}

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

3

제네릭 함수를 지정할 때 다음과 같이 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")

그리고 그것은 컴파일됩니다


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가 Generics를 처리하는 방식은 여전히 ​​헷갈립니다.
  • 이 예와의 해결 방법 struct이 좋은 예입니다. 여기의 해결 방법은 더 많은 정보가 없지만 컴파일러를 전달합니다. 동일한 정보이지만 다른 결과? 그렇다면 뭔가 잘못되었습니다. 컴파일러 버그 인 경우 수정 될 수 있습니다.

0

내 일반 클래스 함수와 비슷한 문제가 있습니다 class func retrieveByKey<T: GrandLite>(key: String) -> T?.

let a = retrieveByKey<Categories>(key: "abc")Categories가 GrandLite의 하위 클래스 라고 부를 수 없었 습니다.

let a = Categories.retrieveByKey(key:"abc")Categories가 아닌 GrandLite를 반환했습니다. 일반 함수는 호출하는 클래스를 기반으로 유형을 추론하지 않습니다.

class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?내가 시도했을 때 let a = Categories.retrieveByKey(aType: Categories, key: "abc")Categories.Type이 GrandLite의 하위 클래스 임에도 불구하고 Categories.Type을 GrandLite로 변환 할 수 없다는 오류가 발생했습니다. 하나...

class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T? 한 내가 노력하면 일을 let a = Categories.retrieveByKey(aType: [Categories](), key: "abc")분명히 작동하지 않는 서브 클래스의 명시 적 할당을하지만, 다른 제네릭 형식 (배열)를 사용하여 암시의 할당은 스위프트 3에서 작업을 수행합니다.


1
오류를 수정하려면 자체 를 입력하는 대신의 aType인스턴스 로 제공해야 합니다. 예 : . 또 다른 해결책은 define 입니다. 그런 다음 방법으로 호출TCategorieslet a = Categories.retrieveByKey(aType: Categories(), key: "abc")aType: T.Typelet a = Categories.retrieveByKey(aType: Categories.self, key: "abc")
— nahung89
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.