“不能接收”地址“和“不能调用指针方法” [英] "cannot take the address of" and "cannot call pointer method on"

查看:200
本文介绍了“不能接收”地址“和“不能调用指针方法”的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个编译和工作:

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

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

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

有人能帮我理解为什么吗? (学习Go)



以下是这些方法:

  // Minus减去另一个向量
func(a * Vector3)减号(b Vector3)Vector3 {
return Vector3 {aX - bX,aY - bY,aZ - bZ}
}

// Normalize使向量长度为​​1
func(a * Vector3)Normalize()Vector3 {
d:= a.Length()
return Vector3 {aX / d,aY / d,aZ / d}
}


解决方案

Vector3.Normalize()方法有一个指针接收器,所以为了调用这个方法,一个指向 Vector3 值是必需的( * Vector3 )。在第一个示例中,将 Vector3.Minus()的返回值存储在变量中,该变量的类型为 Vector3



Go中的变量是可寻址的,当您编写 diff.Normalize()时,这是一个快捷方式,并且编译器会自动获取 diff 变量的地址,以使所需的接收器值类型为 * Vector3 in为了调用 Normalize()。因此,编译器会将它转换为
$ b $ $ p $ (& diff).Normalize()

详情请参阅 Spec:Calls:


方法调用 xm()是如果 x 方法集有效c $ c>包含 m ,参数列表可以分配给 m 参数列表。如果 x 寻址& x 的方法集包含 m xm()(& x).m()


为什么你的第二个例子不起作用是因为函数和方法调用的返回值是不可寻址的,所以编译器不能在这里做同样的事情,编译器无法获取地址 Vector3.Minus()调用的返回值。



什么是可寻址的,完全列在规格:地址运算符:


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

请参阅相关问题:

如何从函数调用获取返回值的指针?



如何我可以在Go中存储对操作结果的引用吗?



可能的解决方法



最简单(要求最少更改)仅仅是指定给一个变量,然后调用该方法。这是你的第一个工作解决方案。另一种方法是修改方法,使其具有值接收器(而不是指针接收器),这样就没有必要采取这些方法的返回值的地址,因此可以链接调用。请注意,如果一个方法需要修改接收器,这可能是不可行的,因为只有当它是一个指针时才有可能(因为接收器像其他参数一样被传递 - 通过复制 - 并且如果它不是指针,你只能修改副本)。

另一种方法是修改返回值以返回指针( * 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:= projection.Minus(c.Origin).pv( ).Normalize()

一些注释:



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


This compiles and works:

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

This does not (yields the errors in the title):

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

Can someone help me understand why? (learning Go)

Here are those methods:

// 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}
}

解决方案

The Vector3.Normalize() method has a pointer receiver, so in order to call this method, a pointer to Vector3 value is required (*Vector3). In your first example you store the return value of Vector3.Minus() in a variable, which will be of type Vector3.

Variables in Go are addressable, and when you write diff.Normalize(), this is a shortcut, and the compiler will automatically take the address of the diff variable to have the required receiver value of type *Vector3 in order to call Normalize(). So the compiler will "transform" it to

(&diff).Normalize()

This is detailed in Spec: Calls:

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m().

The reason why your second example doesn't work is because return values of function and method calls are not addressable, so the compiler is not able to do the same here, the compiler is not able to take the address of the return value of the Vector3.Minus() call.

What is addressable is exactly listed in the Spec: Address operators:

The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x [in the expression of &x] may also be a (possibly parenthesized) composite literal.

See related questions:

How to get the pointer of return value from function call?

How can I store reference to the result of an operation in Go?

Possible "workarounds"

"Easiest" (requiring the least change) is simply to assign to a variable, and call the method after that. This is your first working solution.

Another way is to modify the methods to have a value receiver (instead of pointer receiver), so that there is no need to take the address of the return values of the methods, so calls can be "chained". Note that this might not be viable if a method needs to modify the receiver, as that is only possible if it is a pointer (as the receiver is passed just like any other parameters – by making a copy –, and if it's not a pointer, you could only modify the copy).

Another way is to modify the return values to return pointers (*Vector3) instead of Vector3. If the return value is already a pointer, no need to take its address as it's good as-is for the receiver to a method that requires a pointer receiver.

You may also create a simple helper function which returns its address. It could look something like this:

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

Using it:

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

This could also be a method of Vector3, e.g.:

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

And then using it:

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

Some notes:

If your type consists of 3 float64 values only, you should not see significant performance differences. But you should be consistent about your receiver and result types. If most of your methods have pointer receivers, so should all of them. If most of your methods return pointers, so should all of them.

这篇关于“不能接收”地址“和“不能调用指针方法”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆