我想定义一个具有相关类型的协议,该协议必须是特定基类的子类。显然,假设我有类似下面的例子。
class C<T> {
init(arg: T) {}
}
class SC<T>: C<T> {}
protocol P {
associatedtype A: C<Self>
func foo(_ x: A)
}
struct D: P {
typealias A = C<D>
func foo(_ x: A) {}
}
struct E: P {
typealias A = SC<E>
func foo(_ x: A) {}
}
我希望这段代码能够正确编译。相反,我得到错误Type 'D' does not conform to protocol 'P'
和Type 'E' does not conform to protocol 'P'
。 Xcode还提供以下消息:Possibly intended match 'D.A' (aka 'C<D>') does not inherit from 'C<Self>'
和Possibly intended match 'E.A' (aka 'SC<E>') does not inherit from 'C<Self>'
。
这些错误对我没有意义。在第一种情况下,C<D>
应该与C<Self>
相同,所以C<D>
应该符合C<D> == C<Self>
。在第二种情况下,C<Self>
应该是C<E>
,所以SC<E>
应该符合C<E> == C<Self>
。根据Xcode的消息,显然不是这种情况。
这是错误消息的编译器错误/问题,还是我误解了相关类型在这种情况下的工作方式?
我不认为你对相关类型的理解是一个问题,就像泛型一样。泛型不是协变的;参数化类型没有替代原则。换句话说,仅仅因为你可以在精神上插入D
代替Self
并不意味着你可以用C<D>
代替C<Self>
。它们是不同类型(不相关)。
这是一个简单的反例,向您展示我的意思:
class C<T> {
init(arg: T) {}
}
class Cat {}
class Kitten : Cat {}
现在让我们尝试使用C<Kitten>
,预计会有C<Cat>
:
var c1 = C(arg:Cat())
var c2 = C(arg:Kitten())
c1 = c2 // error
我怀疑这是你认为应该工作的。它没有。出于同样的原因,你认为“C<D>
应该与C<Self>
的类型相同”是错误的。
为了使这与您的实际示例更相关,请考虑这个简单的案例。这编译:
class C<T> {
init(arg: T) {}
}
protocol P {
associatedtype A: C<Any>
func foo(_ x: A)
}
struct D: P {
func foo(_ x: C<Any>) {}
}
但这不编译:
class C<T> {
init(arg: T) {}
}
protocol P {
associatedtype A: C<Any>
func foo(_ x: A)
}
struct D: P {
func foo(_ x: C<String>) {}
}
好吧,你可能会说String是一个Any,为什么不编译呢?这是因为这个事实无关紧要;泛型中的参数化类型没有替换原则。