首页 > 解决方案 > 函数名称前带有下划线的结构标记

问题描述

我正在使用 go,特别是 QT 绑定。但是,我不明白在下面的结构中使用前导下划线。我知道一般下划线的使用,但不知道这个具体的例子。

type CustomLabel struct {
    core.QObject

    _ func() `constructor:"init"`
    _ string `property:"text"`
}

它与结构标签有关吗?

标签: gostructnaming-conventions

解决方案


这些被称为空白字段,因为空白标识符用作字段名称。

它们不能被引用(就像任何以空白标识符作为名称的变量一样),但它们参与结构的内存布局。通常并且实际上它们用作填充,以将后续字段与匹配来自(或去往)另一个系统的数据布局的字节位置(或内存位置)对齐。好处是这些结构值(或者更确切地说是它们的内存空间)可以在一个步骤中简单有效地转储或读取。

@mkopriva 的回答详细说明了该问题的具体用例的用途。

一个警告:这些作为“类型注释”的空白字段应该谨慎使用,因为它们给这种结构的所有(!)值增加了不必要的开销。这些字段不能被引用,但它们仍然需要内存。如果您添加一个大小为 8 个字节的空白字段(例如int64),如果您创建一百万个元素,那么这 8 个字节将计数一百万次。因此,这是对空白字段的“错误”使用:目的是将元信息添加到类型本身(而不是其实例),但代价是所有元素都需要增加内存。

您可能会说使用大小为 0 的类型,例如struct{}. 更好的是,好像用在正确的位置(例如,作为第一个字段,如果字段顺序不同,请参见 Struct 具有不同的大小,以及为什么结构中的 `[0]byte` 的位置很重要?),他们赢了'不要改变结构的大小。尽管如此,使用反射来迭代结构字段的代码仍然必须循环这些字段,因此它会降低此类代码的效率(通常是所有编组/解组过程)。此外,由于现在我们不能使用任意类型,因此我们失去了携带类型信息的优势。

最后一条语句(关于使用时struct{}我们丢失了携带的类型信息)可以被规避。struct{}不是唯一大小为 0 的类型,所有长度为 0 的数组的大小也为零(无论实际元素类型如何)。所以我们可以通过使用我们想要合并的类型的 0 大小的数组来保留类型信息,例如:

type CustomLabel struct {
    _ [0]func() `constructor:"init"`
    _ [0]string `property:"text"`
}

现在这种CustomLabel类型在性能方面看起来比所讨论的类型要好得多:它的大小仍然是 0。并且仍然可以使用Type.Elem()以下示例访问数组的元素类型:

type CustomLabel struct {
    _ [0]func() `constructor:"init"`
    _ [0]string `property:"text"`
}

func main() {
    f := reflect.ValueOf(CustomLabel{}).Type().Field(0)
    fmt.Println(f.Tag)
    fmt.Println(f.Type)
    fmt.Println(f.Type.Elem())
}

输出(在Go Playground上试试):

constructor:"init"
[0]func()
func()

有关结构标签的概述,请阅读相关问题:Go 中标签的用途是什么?


推荐阅读