2014-09-29 5 views
8

я определил две общие функцииКак вызвать неоднозначную общую функцию в Swift?

func job<T: Comparable>(x: T) { 
    println("1") 
} 

func job<T: Hashable>(x: T) { 
    println("2") 
} 

и когда я пытаюсь вызвать один из них, например, с:

let myInt: Int = 1 // Explicit Int just for clarity of the example 
job(myInt) 

конечно Swift жалуется и выдает ошибку
Неоднозначность использования от «вакансии»
Это понятно, потому что неясно, хочу ли я использовать Сопоставимый один или Hashable (Int соответствует обоим)

Есть ли способ я могу намекнуть компилятор, который один я хочу использовать?

+1

Работает ли 'job (myInt as Hashable)' работа? –

+1

Nope :(Я получаю 2 ошибки: 'Протокол 'Hashable' может использоваться только как общее ограничение, потому что он имеет собственные или ассоциированные требования типа. И' Type 'Hashable' не соответствует протоколу 'Comparable'' (это один звучит странно :)) –

+0

FYI для последней быстрой заметки эти два отличных ответа ... http://stackoverflow.com/a/39836054/294884 ... http://stackoverflow.com/a/39835658/294884 – Fattie

ответ

10

Это неоднозначно, потому что Int является как Hashable, так и Comparable, и ни один из этих двух протоколов не находится в одной иерархии. (Вы можете просмотреть Intprotocol hierarchy on Swifter.)

func f<T: Hashable>(t: T) { 
    println("Hashable: \(t)") 
} 
func f<T: Comparable>(t: T) { 
    println("Comparable: \(t)") 
} 

let number = 5 
f(number) 
// error: ambiguous use of 'f' 

Вы не можете явно указать, какие функции для вызова, из-за требований, связанных типа каждого протокола, но то, что вы можете сделать, это определить третью функцию :

func f<T: Comparable where T: Hashable>(t: T) { 
    println("Both Hashable & Comparable: \(t)") 
} 
f(number) 
// Both Hashable & Comparable: 5 

Это как Swift implements..< оператор, который в противном случае был бы неоднозначным для типов, реализующих как Comparable и ForwardIndexType.


Чтобы расширить немного дальше, вот посмотрите на то, что я имел в виду «Вы не можете явно указать, какие функции для вызова из-за требований, связанных типа каждого протокола.» Протоколы могут быть использованы в качестве типов, как описано в книге Swift chapter on Protocols:

protocol RandomNumberGenerator { 
    func random() -> Double 
} 

class Dice { 
    let generator: RandomNumberGenerator 
    // ... 
} 

В этом примере свойство генератора может быть любого типа, который соответствует RandomNumberGenerator - подобно тому, как id<ProtocolName> используется в Objective-C. Однако протоколы могут использоваться только как типы, если они не включают связанный тип или ссылку Self в свою декларацию. К сожалению, это исключает почти каждый встроенный тип Swift, включая Hashable и Comparable.

Hashable наследует от Equatable, который ссылается на Self при определении == оператора:

func ==(lhs: Self, rhs: Self) -> Bool 

и Comparable делает то же самое с его операторами:

func <=(lhs: Self, rhs: Self) -> Bool 
// similar definitions for <, >, and >= 

Эти протоколы могут быть использованы только, как общие ограничения и не используются в качестве типа при объявлении переменной. (Насколько я могу судить, это недокументировано, но можно обнаружить через сообщения об ошибках.)

два протокола, которые сделать не есть, что ограничение является Printable и BooleanType, так что мы можем посмотреть, как они работают. Bool является единственным встроенным типом, который соответствует BooleanType, а также Printable, так что это будет наш тип теста. Вот наши общие функции p() и переменная t - обратите внимание, что, как и прежде, мы не можем просто вызвать функцию с t:

func p<T: Printable>(t: T) { 
    println("Printable: \(t)") 
} 
func p<T: BooleanType>(t: T) { 
    println("BooleanType: \(t)") 
} 

let t: Bool = true 
p(t) 
// error: Ambiguous use of 'p' 

Вместо этого мы должны бросить (вентиляционный?) t к определенному протоколу используя as ключевое слово, и называть конкретную родовую функцию таким образом:

p(t as Printable) 
// Printable: true 

p(t as BooleanType) 
// BooleanType: true 

так что, пока у нас есть квалификационный протокол, мы можем выбрать, какой вариант универсального метода для вызова.

+1

Чтобы уточнить, методы «Comparable» и «Hashable» должны были просто продемонстрировать проблему. Я не ожидаю, что они понадобятся, но я хотел понять, насколько я ограничен в будущем. Теперь я не уверен, что вы подразумеваете под «Вы не можете явно указать, какую функцию вызывать, ** из-за связанных требований типа к каждому протоколу **». Есть ли какая-то фундаментальная проблема здесь, когда вы можете предоставить компилятору подсказку о том, какой метод использовать? Считаете ли вы, что мы можем ожидать такой возможности в будущем? –

+0

Не говорит компилятору, который может использовать вид в противоположность дизайну универсального программирования (в общем, не только в Swift)? Независимо от того, есть ли функция, которую вы хотите увидеть (или, по крайней мере, не ожидайте) в будущем, вероятно, лучше [указать ошибку] ​​(http://bugreport.apple.com). – rickster

+0

@BartekChlebek: В этот момент добавлено немного (хорошо, много) объяснений. Свифт, безусловно, в движении, но я не обязательно вижу, что он должен меняться. Такой конфликт кажется относительно редким, нет? –