为什么 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。如果 vector
是 nil
,那么 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 个参数也更清楚哪个是左操作数,哪个是右操作数。
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。如果 vector
是 nil
,那么 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 个参数也更清楚哪个是左操作数,哪个是右操作数。