一尘不染

“无法获取地址”和“无法调用指针方法”

go

可以编译并运行:

diff := projected.Minus(c.Origin)
dir := diff.Normalize()

这不会(产生标题中的错误):

dir := projected.Minus(c.Origin).Normalize()

有人可以帮我理解为什么吗?(学习围棋)

这些是这些方法:

// Minus subtracts another vector from this one
func (a *Vector3) Minus(b Vector3) Vector3 {
    return Vector3{a.X - b.X, a.Y - b.Y, a.Z - b.Z}
}

// Normalize makes the vector of length 1
func (a *Vector3) Normalize() Vector3 {
    d := a.Length()
    return Vector3{a.X / d, a.Y / d, a.Z / d}
}

阅读 212

收藏
2020-07-02

共1个答案

一尘不染

Vector3.Normalize()方法有一个 指针
接收器,因此要调用此方法,需要一个指向Vector3值的指针(*Vector3)。在第一个示例中,您将的返回值存储Vector3.Minus()在类型为的变量中Vector3

Go中的变量是可寻址的,并且在编写时diff.Normalize(),这是一个快捷方式,编译器将自动获取diff变量的地址,以具有必需的类型的接收者值*Vector3以进行调用Normalize()。因此,编译器会将其“转换”为

(&diff).Normalize()

在“
规范”中对此进行了详细说明

x.m()如果方法集(的类型)x包含m并且参数列表可以分配给的参数列表,则该方法调用有效m。如果x可寻址&x方法集包含mx.m()则为的简写(&x).m()

您的第二个示例不起作用的原因是因为函数和方法调用的返回值 不可寻址
,因此编译器在此处无法执行同样的操作,编译器无法采用该函数的返回值的地址。Vector3.Minus()呼叫。

在“
规范:地址”运算符中准确列出了可寻址的内容

操作数必须是 可寻址的 ,即变量,指针间接 寻址
或切片索引操作;或可寻址结构操作数的字段选择器;或可寻址数组的数组索引操作。作为可寻址性要求的例外,x[在表达式中&x]也可以是(可能带有括号的)复合文字

可能的“解决方法”

“最简单”(需要最少的更改)只是分配给变量,然后在此之后调用方法。这是您的第一个可行的解决方案。

另一种方法是将方法修改为具有值接收器(而不是指针接收器),从而无需获取方法返回值的地址,因此可以将调用“链接”。请注意,如果某个方法需要修改接收方,则这可能不可行,因为只有当它是指针时才可能这样做(因为接收方就像通过传递副本一样通过任何其他参数传递,并且如果它不是指针,则被传递,则只能修改副本)。

另一种方法是将返回值修改为返回指针(*Vector3)而不是Vector3。如果返回值已经是一个指针,则不需要使用它的地址,因为对于需要接收者的方法来说,接收器的状态是良好的。

您也可以创建一个简单的帮助函数,该函数返回其地址。它可能看起来像这样:

func pv(v Vector3) *Vector3 {
    return &v
}

使用它:

dir := pv(projected.Minus(c.Origin)).Normalize()

这也可以是的方法Vector3,例如:

func (v Vector3) pv() *Vector3 {
    return &v
}

然后使用它:

dir := projected.Minus(c.Origin).pv().Normalize()

一些注意事项:

如果您的类型float64仅包含3个值,则不应看到明显的性能差异。但是您应该对接收者和结果类型保持一致。如果您的大多数方法都具有指针接收器,那么所有方法都应该具有。如果您的大多数方法都返回指针,那么所有方法也都应返回。

2020-07-02