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


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.