Как сравнить две функции на равенство указателей в последнем еженедельнике Go? - PullRequest
26 голосов
/ 10 марта 2012

В 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 для получения дополнительной информации и возможного альтернативного решения.

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

Ответы [ 3 ]

43 голосов
/ 10 марта 2012

Обратите внимание, что существует разница между равенством и идентичностью.Операторы == и != в 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
}

полагается на неопределенное поведение .Нет никаких гарантий относительно того, что программа будет печатать.Компилятор может решить, что он объединит SomeFun и AnotherFun в одну реализацию, и в этом случае 2-й оператор print выведет true.На самом деле, нет абсолютно никакой гарантии, что 1-й оператор печати будет печатать 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
}
3 голосов
/ 10 марта 2012

Обходной путь зависит от ситуации. Мне пришлось поменять пару мест, где я сравнивал функции. В одном случае я просто сделал что-то другое, чтобы мне больше не нужно было их сравнивать. В другом случае я использовал структуру, чтобы связать функции с сопоставимыми строками, что-то вроде

type nameFunc struct {
    name string
    fval func()
}

У меня было только несколько функций, которые мне нужно было сравнить, поэтому было проще всего сохранить фрагмент этих структур и отсканировать фрагмент по необходимости, сравнив поле имени и отправив команду fval. Если у вас их очень много, вы можете использовать карту. Если ваши функции имеют разные подписи, вы можете использовать интерфейсы и т. Д.

1 голос
/ 10 марта 2012

еженедельно 2011-11-18

Сравнение карт и значений функций теперь запрещено (за исключением сравнения с нулем) в соответствии с планом Go 1.Равенство функций было проблематичным в некоторых контекстах, и равенство карт сравнивает указатели, а не содержимое карт.

Равенство

Равенство функций было проблематичным при наличии замыканий (когдадва замыкания равны?)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...