34

在 Go 中,有没有办法比较两个非零函数指针来测试是否相等?我的平等标准是指针平等。如果不是,是否有任何特殊原因不允许指针相等?

到目前为止,如果我尝试以直接的方式执行此操作:

package main

import "fmt"

func SomeFun() {
}

func main() {
    fmt.Println(SomeFun == SomeFun)
}

我明白了

./func-pointers.go:12: invalid operation: SomeFun == SomeFun (func can only be compared to nil)

据我了解,这种行为是最近引入的。


我使用反射包找到了答案;但是 Atom 在下面建议这实际上会产生未定义的行为。有关更多信息和可能的替代解决方案,请参阅 Atom 的帖子。

package main

import "fmt"
import "reflect"

func SomeFun() { }

func AnotherFun() { }

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())
}

输出:

true
false
4

3 回答 3

55

请注意,平等和身份之间存在差异。Go1 中的运算符==!=正在比较等价的值(比较通道时除外),而不是身份。因为这些算子试图混合平等和身份,所以 Go1 在这方面比 pre-Go1 更加一致。

函数相等不同于函数标识。


不允许使用函数类型的一个原因==!=性能。例如,以下闭包不使用其环境中的任何变量:

f := func(){fmt.Println("foo")}

禁止函数比较使编译器能够为闭包生成单个实现,而不是要求运行时创建新的闭包(在运行时)。因此,从性能角度来看,禁止函数比较的决定是一个不错的决定。


关于使用reflect包来确定函数身份,类似的代码

func SomeFun()    {}
func AnotherFun() {}

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())  // Prints true

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())  // Prints false
}

依赖于未定义的行为。无法保证程序将打印什么。编译器可能会决定将其合并SomeFunAnotherFun一个实现,在这种情况下,第二个 print 语句将 print true。事实上,绝对不能保证第一条 print 语句会打印true(在其他一些 Go1 编译器和运行时,它可能会打印false)。


您原来的问题的正确答案是:

package main

import "fmt"

func F1() {}
func F2() {}

var F1_ID = F1  // Create a *unique* variable for F1
var F2_ID = F2  // Create a *unique* variable for F2

func main() {
    f1 := &F1_ID  // Take the address of F1_ID
    f2 := &F2_ID  // Take the address of F2_ID

    // Compare pointers
    fmt.Println(f1 == f1)  // Prints true
    fmt.Println(f1 == f2)  // Prints false
}
于 2012-03-10T07:40:23.907 回答
6

解决方法取决于情况。我不得不更改几个比较函数的地方。在一次情况下,我只是做了一些不同的事情,所以我不需要再比较它们了。在另一种情况下,我使用结构将函数与可比较的字符串相关联,例如,

type nameFunc struct {
    name string
    fval func()
}

我只有几个需要比较的函数,所以最简单的方法是保留这些结构的一个切片并根据需要扫描切片,比较名称字段并调度 fval。如果你有很多,你可以用地图代替。如果你的函数有不同的签名,你可以使用接口,等等。

于 2012-03-10T04:23:15.107 回答
1

每周.2011-11-18

根据 Go 1 计划,现在不允许映射和函数值比较(与 nil 比较除外)。函数相等在某些情况下是有问题的,并且映射相等比较指针,而不是映射的内容。

平等

在存在闭包的情况下,函数相等是有问题的(两个闭包何时相等?)

于 2012-03-10T03:28:32.333 回答