30

我已经看到了这个Swift Equatable Protocol问题的答案,其中提到了==必须如何在全局范围内声明该方法。

如果我不采用Equatable,我仍然可以声明==测试我的两种类型之间的相等性。

// extension Foo: Equatable {}

func ==(lhs: Foo, rhs: Foo) -> Bool {
    return lhs.bar == rhs.bar
}

struct Foo {
    let bar:Int
}

事实上,它的实现需要在全局范围内声明,这使得它看起来协议无关,即使Equatable被采用。

Equatable协议除了让(我们和)编译器安全地知道我们的类型实现了协议所需的方法之外,还有什么比语法糖更重要的东西吗?

为什么必须全局声明操作符实现,即使对于协议也是如此?这是由于派遣操作员的某种不同方式造成的吗?

4

5 回答 5

22

更新

来自 Xcode 8 beta 4 发行说明:

运算符可以在类型或其扩展中定义。例如:

struct Foo: Equatable {
    let value: Int
    static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs.value == rhs.value
    }
}

此类运算符必须声明为static(或在一个类中class final),并且与它们的全局对应项具有相同的签名。作为此更改的一部分,还必须明确声明协议中声明的操作员要求static

protocol Equatable {
    static func ==(lhs: Self, rhs: Self) -> Bool
}

原来的

最近(2016-01-31 到 2016-02-09 到目前为止)在 swift-evolution 列表中对此进行了讨论。以下是 Chris Lattner 所说的,关于在结构或类范围内声明运算符:

是的,这是一个普遍需要的特性(至少对于对称运算符而言)。这对于在类声明中动态调度操作符也很好。我不认为我们有一个明确的建议来确定名称查找如何与此一起工作。

后来(回复 Haravikk):

什么是名称查找问题?您是指 Foo == Foo 的运算符存在于多个位置的情况吗?

是的。名称查找必须有一个明确定义的搜索顺序,它定义了遮蔽和无效的多重定义规则。

就我个人而言,我会坚持我们现在所拥有的,即将特定类/结构中的运算符实现视为全局定义,如果多次声明相同的签名,则会引发错误。

我们需要多个模块来定义运算符的实例,我们需要扩展中的运算符,并且我们需要追溯一致性才能工作,就像任何其他成员一样。

于 2016-02-10T20:34:56.823 回答
7

文档中的解释

运算符函数被定义为一个全局函数,其函数名称与要重载的运算符相匹配。

该函数是全局定义的,而不是作为目标类或结构上的方法,因此它可以用作目标类或结构的现有实例之间的中缀运算符。

Vector2D我将引用中的具体结构 ( ) 的名称替换为泛型表达式目标类或结构

于 2016-02-10T20:26:13.560 回答
7

即使采用了 Equatable,它的实现需要在全局范围内声明这一事实,也使它看起来与协议无关且与协议不同。

每个协议都是如此,无论它需要全局函数、类方法还是实例方法。您始终可以独立于是否有需要它的协议来实现事物。

Equatable 协议除了让(我们和)编译器安全地知道我们的类型实现了协议所需的方法之外,还有什么比语法糖更重要的吗?

那不是糖。这就是协议的定义和协议的全部意义。它告诉你这个类型有可用的这些东西可以应用到它上面。

为什么必须全局声明操作符实现,即使对于协议也是如此?这是由于派遣操作员的某种不同方式造成的吗?

因为那是 Swift 语法。运算符函数实现必须是全局函数。Swift 团队一直有兴趣改变它以使其更加一致,但今天这正是 Swift 的工作方式。

于 2016-02-10T20:35:53.827 回答
1

在 ie 之前它有点像糖,因为您需要手动执行Equatable. 尽管 Rob Napier 提出了一个很好的观点,那就是协议的用途。

但情况不再如此,即仅通过一致性您就可以获得自动综合并且不再需要样板代码。

struct Country: Equatable {
  let name: String
  let capital: String
  var visited: Bool
}

而已!编译器将完成其余的工作。

let france = Country(name: "France", capital: "Paris", visited: true)
let spain = Country(name: "Spain", capital: "Madrid", visited: true)
if france == spain { ... } // false

话虽如此,如果您愿意,您仍然可以覆盖该==函数的实现。

更多请看这里

于 2018-08-22T20:43:07.457 回答
0

在 Swift 标准库中,定义了 '==' 运算符。它对比较的一侧或另一侧没有关联性,并且在整个 Swift 运算符顺序中具有编号优先级。如果您在“import Swift”上 CMD-click Swift,您可以检查这一点

infix operator == {
    associativity none
    precedence 130
}

对于 Int,该库已经将 Int/Int8/Int16/Int32/Int64 作为 Equatable:

public func ==(lhs: Int, rhs: Int) -> Bool

就像许多其他类型一样。

Int 在扩展中成为 Hashable (要符合该协议,您需要创建一个 var hashValue: Int { get } ,它会随着应用程序的每次执行而变化:

extension Int : Hashable {
    public var hashValue: Int { get }
}

操作符 '==' 和协议通常需要在顶层的 struct/enum/class 之外声明,因为您正在扩展语言,而现在,这就是 swift 的编译方式;所有 Swift 的操作符都是在全局范围内定义的属性。

对于您的“Foo”,您通过添加函数采用了 Equatable:

func ==(lhs: Foo, rhs: Foo) -> Bool {
    return lhs.bar == rhs.bar
}

它使用协议中的“Self”局部变量:

public protocol Equatable {
    public func ==(lhs: Self, rhs: Self) -> Bool
}
于 2016-02-15T01:12:10.383 回答