为什么 Swift 运算符重载需要是类型方法?

Why do Swift operator overloads need to be type methods?

The Swift Language Guide表示像下面这样的操作符方法必须写成static:

struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector

如果去掉static关键字,会导致编译错误:

Operator '+' declared in extension of 'Vector2D' must be 'static'

这个要求的原因是什么?为什么我们不能把它写成非静态方法?

我可以盲目地接受“这就是它完成的方式”。但我想了解为什么这是必要的,以便我可以更好地理解 Swift 语言。我没能找到这个要求的具体原因。

如果非要我猜的话,我猜 Swift 中的默认运算符是作为类型方法实现的,因此我们的自定义运算符也必须是静态的。但这纯粹是猜测。

这对我来说非常有意义。二元运算符将 2 个对象作为参数,returns 一个结果。

这样的运算符不属于任何对象的实例。它是 class 上的静态方法。对于 Vector2D 类型的 + 方法示例,它是一种知道如何将 2 个 Vector2D 对象加在一起的方法。

您没有在 Vector2D 的实例上调用 + 方法。你说“嘿,Vector2D类型,我如何将你的类型的两个实例加在一起?

您正在与 class“交谈”,而不是 class 的实例。

编辑:

如果它是实例方法,则必须将加法运算符表示为对操作数之一的消息:

extension Vector2D {
    func addVector(_: Vector2D) -> Vector2D {
        // Code to sum two vectors
    }
}

你会称它为

sum = aVector.addVector(anotherVector)

这是一种语言设计选择,不同的语言决定了对立面:C++ 不允许静态运算符,而 C# 要求它们是静态的,就像 Swift.

对我来说,让运算符静态的最令人信服的理由是对称性

让我们暂时用名为 plus 的函数替换 +。如果 plus 是静态的,您可以将其称为 Vector2D.plus(vector, anotherVector),这与调用 Vector2D.plus(anotherVector, vector).

相同

如果它是一个实例成员,您会称它为 vector.plus(anotherVector),它可能与 anotherVector.plus(vector) 相同,也可能不同。 IE。如果 vectornil,那么 vector.plus(anotherVector) 肯定是 nil,而 anotherVector.plus(vector) 将等于 anotherVector

类似地,运算符 + 是静态的允许 vector + anotherVector == anotherVector + vector,无论两个向量的 nil 状态如何。

运算符不必是静态的。它们也可以是全局函数。

struct Vector2D {
    var x = 0.0, y = 0.0
}

func + (left: Vector2D, right: Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y + right.y)
}

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector

如果你问为什么它们不能是 instance 方法,那么,因为实例方法需要调用额外的 Vector2D 实例。那个实例加上 + 需要的两个参数,意味着你需要 3 Vector2D 来调用 +,这没有多大意义...

另一方面,运算符可以这样设计:

extension Vector2D {
    func +(right: Vector2D) -> Vector2D { ... }
}

二元运算符不是对两个参数进行运算,而是对 self 和单个参数进行运算。这类似于 Kotlin 的运算符重载的设计方式。

据我所知,Swift 中没有任何内容可以阻止 Swift 被设计成这样。这样做的原因很可能是美学,比如“有 2 个参数更有意义”,正如 Duncan C 所建议的,并且有 2 个参数也更清楚哪个是左操作数,哪个是右操作数。