golang 从 map 获取值时的值拷贝问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang 从 map 获取值时的值拷贝问题相关的知识,希望对你有一定的参考价值。

参考技术A 我们知道 golang 中,slice, map, channel 是引用类型,函数之间传递都是以值拷贝的形式进行的,引用类型经过函数传递,依然是引用类型。
在上述例子中,我们从 map 中想拿出一个值,这个值是一个简单结构体,拿出这个值后,不确定这个值和 map 中的值是什么关系,如果不小心修改,是否会造成 map 值变更。
我们希望 golang 中更多的是值传递,这样能避免数据存储在堆上,造成 gc 负担。

可以看到,修改值后,map 中的值保持不变。说明 map 获取的值也是值传递出来的。

从变体中获取值时的 boost::get vs boost::apply_visitor

【中文标题】从变体中获取值时的 boost::get vs boost::apply_visitor【英文标题】:boost::get vs boost::apply_visitor when fetching values from a variant 【发布时间】:2019-01-04 03:34:23 【问题描述】:

我们在生产代码中广泛使用 boost::variant 的集合。 我们从这个集合中提取值的方式是

for (auto & var : vars)

    switch (var.which())
    
        case 1 :
        AVal = boost::get<A>(var);
        break;
        case 2 :
        BVal = boost::get<B> (var);
        ...
      

阅读有关变体的更多信息,我可以看到另一种选择

for (auto & var : vars)

    switch (var.which())
    
        case 1 :
        AVal = boost::apply_visitor(AVisitor, var);
        break;
        case 2 :
        BVal = boost::apply_visitor(BVisitor, var);
        ...
     

忽略 apply_visitor 提供编译时类型安全值访问并且功能更强大的事实,我是否应该期望上述任何一种方法在运行时性能方面有任何差异?

【问题讨论】:

这两个对我来说都是代码味道。您没有在访问者中进行工作是否有原因? 当你说“不在访问者内部做工作”是什么意思?这只是一个虚拟示例,在实际情况下,如果您的意思是这样,A/BVisitor 类将有适当的实现...... 不要为不懂的东西写伪代码。我看不出有什么好的理由在访问之前进行切换; 访问是一种切换操作。到我将您的伪代码翻译成不是废话的代码时,它与您在真实代码中所做的事情几乎没有共同之处,除非非常幸运。 minimal reproducible example 请。 【参考方案1】:

boost::variant 只是一个与您提供的最大数据类型对齐的内存块,以及一个指定当前使用这些类型中的哪一个的整数。还有很多允许访问逻辑的编译时宏。

放弃一两次运行时检查以确保获取正确的类型,访问该内存位置应该没有其他成本,重新解释为所需的类型。

【讨论】:

但是如果非要比较以上两种方法的话。在性能方面,哪个更可取? boost::get 会稍微好一点。情况就是这样,因为boost::apply_visitor 需要实例化访问者,然后将其应用于成员,boost::get 不必这样做。但是,我每次都会选择boost::apply_visitor。再调用一个函数就可以为类型安全和干净的代码付出更公平的代价。 @aybassiony 我观察到(通过调试)是 boost::get() 和我对 apply_visitor 的定义,两者最终都执行相同的元编程开关。唯一的区别是 boost::get() 使用并因此实例化了通用访问者,而在后一种情况下,它是被调用的明确定义的访问者。另外,我对两者进行了性能比较,apply_visitor 似乎更快。

以上是关于golang 从 map 获取值时的值拷贝问题的主要内容,如果未能解决你的问题,请参考以下文章

go map and slice 2021-10-08

Golang教程:Map

GoLang基础数据类型---字典

golang/go语言time.NewTimer()和time.After()两个计时器

golang/go语言time.NewTimer()和time.After()两个计时器

golang/go语言time.NewTimer()和time.After()两个计时器