【发布时间】:2021-03-20 10:07:51
【问题描述】:
有点学术,但看这段代码
// Some protocol
protocol R : Equatable
{
}
// Some class
class E : R
{
// Default implementation
static func == ( lhs : E, rhs : E ) -> Bool
{
print ( "Comparing E ===" )
return lhs === rhs
}
}
// Some class with a string
class F : E
{
let f : String
init ( f : String )
{
self.f = f
}
// Copare strings
static func == ( lhs : F, rhs : F ) -> Bool
{
print ( "Comparing F ==" )
return lhs.f == rhs.f
}
}
// Some generic container type
class G < T : R > : R
{
let g : T
init ( g : T )
{
self.g = g
}
// Compare
static func == ( lhs : G, rhs : G ) -> Bool
{
print ( "Comparing G ==" )
return lhs.g == rhs.g
}
}
let f1 = F ( f : "abc" )
let f2 = F ( f : "abc" )
print ( "f1 == f2 ? \( f1 == f2 ) (expect true)" )
let g1 = G < F > ( g : f1 )
let g2 = G < F > ( g : f2 )
print ( "g1 == g2 ? \( g1 == g2 ) (expect true)" )
这给了
Comparing F ==
f1 == f2 ? true (expect true)
Comparing G ==
Comparing E ===
g1 == g2 ? false (expect true)
代码定义了一些协议,然后是一个实现它并提供通用比较器的类E。接下来是F,它会覆盖这个比较器来比较它所包含的字符串f。最后是一些容器类G,其中包含实现协议的东西。
到目前为止一切顺利。现在让我们比较一下东西。与 F 的实例进行比较,因为使用了正确的比较器。然而,比较两个容器类是行不通的,而且看起来 Swift 调用太浅了。它调用E 中提供的通用实现,而不是F 中覆盖的更深层次的实现。如果这是 Objective-C,即使 lhs 和 rhs 是不同的类,它也会进行更深入的调用,然后它通常会崩溃。 Swift 不允许这样做,这很容易通过创建另一个类来查看,例如包含 Int 而不是 String 但同时它似乎没有使用正确的比较器。
编辑
这使情况变得更糟。下面的变化是f1、f2 和g1 和g2 被更一般地定义为(或包含)超类类型E。即使F 覆盖了比较器,它也不会在任何比较中使用。
let f1 : E = F ( f : "abc" )
let f2 : E = F ( f : "abc" )
print ( "f1 == f2 ? \( f1 == f2 ) (expect true)" )
let g1 = G < E > ( g : f1 )
let g2 = G < E > ( g : f2 )
print ( "g1 == g2 ? \( g1 == g2 ) (expect true)" )
我怀疑这与运算符重载与函数重载有关......但任何光线都会受到赞赏。
编辑 2
玩弄了一点some,想如果我使用它,也许它可以让它按我想要的方式工作,但目前不允许some 用作函数参数。
【问题讨论】:
-
看看bugs.swift.org/browse/SR-1729,好像有关系。
-
@msbit 看起来像错误,最好的解决方法是实现自己的比较器并避免重载运算符,因为这种想法会严重毒害您的代码
-
@MartinR 刚刚尝试将比较器移动到全局范围作为该帖子中的一个建议,但它保持不变
-
@skaak 很烦,是的,我同意。如果您来自其他 OOP 语言,那么解析路径会非常令人惊讶。
标签: swift