首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Golang中的移动语义

Golang中的移动语义
EN

Stack Overflow用户
提问于 2013-12-31 00:34:23
回答 4查看 3.5K关注 0票数 3

这来自Bjarne的C++编程语言,第四版3.3.2。

我们并不是真的想要一个副本;我们只是想从函数中得到结果:我们想要移动一个向量,而不是复制它。幸运的是,我们可以声明这一意图: 类向量{ // .向量(向量& a);//复制构造向量&operator=(向量& a);//复制赋值向量(向量& a);//移动构造函数向量&operator=(向量& a);//移动赋值}; 给出了这个定义,编译器将选择move构造函数来实现函数外的返回值的传输。--这意味着r=x+y+z将不涉及Vectors的复制。相反,向量只是典型的moved.As,向量的移动构造函数很容易定义.

我知道Golang支持传统的按值传递和引用传递,使用Go样式指针。

Go是否像上面Stroustrup所描述的那样支持“移动语义”,以避免不必要的来回复制?如果是这样的话,这是自动的,还是要求我们在代码中做些什么来实现它。

注意:有几个答案已经发布了--我得消化一下,所以我还没有接受-谢谢。

EN

回答 4

Stack Overflow用户

发布于 2013-12-31 11:24:17

具体情况如下:

  1. 围棋中的一切都是通过价值传递的。
  2. 但是,有五种内置的“引用类型”也是通过值传递的,但在内部它们保存着对单独维护的数据结构的引用:映射、切片、通道、字符串和函数值(无法对后两个引用的数据进行变异)。

你自己的回答@Vector是不正确的,即Go中没有任何东西是通过引用传递的。相反,有些类型具有引用语义。它们的值仍然是通过值传递的(sic!)。

你的困惑来源于这样一个事实:你的头脑目前被C++、Java等所拖累,而Go中的这些事情大多是“像C一样”完成的。

以数组和切片为例。数组在Go中是通过值传递的,但片是一个打包的结构,包含一个指针(指向基础数组)和两个平台大小的整数(切片的长度和容量),当分配或返回时,复制的是这个结构的值--一个指针和两个整数,等等。如果您复制了一个“裸”数组,就会按字面复制它的所有元素。

这同样适用于频道和地图。您可以认为将通道和映射定义为声明的类型如下所示:

代码语言:javascript
复制
type Map struct {
   impl *mapImplementation
}

type Slice struct {
   impl *sliceImplementation
}

(顺便说一句,如果您知道C++,您应该注意到一些C++代码使用这个技巧来降低头文件中的细节暴露。)

所以当你以后

代码语言:javascript
复制
m := make(map[int]string)

您可以将其看作是具有m类型的Map,所以以后您可以这样认为

代码语言:javascript
复制
x := m

m的值被复制,但它只包含一个指针,因此xm现在都引用相同的底层数据结构。m是否通过引用(“移动语义”)复制?当然不是!类型映射值、切片值和通道值是否有引用号?是!

请注意,这三种类型并不特殊:通过在其中嵌入指向某些复杂数据结构的指针来实现自定义类型是一种相当常见的模式。

换句话说,Go允许程序员决定他们想要的类型是什么语义。Go碰巧有五个内置类型,它们已经具有引用语义(而所有其他内置类型都具有值语义)。选择一种语义而不是另一种语义并不会以任何方式影响按值复制所有内容的规则。例如,Go中任何类型的值都可以有指针,并分配它们(只要它们有兼容的类型)--这些指针将被值复制。

从另一个角度来看,很多Go包(标准的和第三方的)更喜欢使用指向(复杂)值的指针。一个例子是os.Open() (它在文件系统上打开一个文件),返回*os.File类型的值。也就是说,它返回一个指针,并期望调用代码传递这个指针。当然,Go作者可能已经声明os.File是包含单个指针的struct,本质上使该值具有引用语义,但他们没有这样做。我认为这是因为没有特殊的语法来处理这种类型的值,所以没有理由让它们作为映射、通道和切片工作。换句话说,亲吻。

建议阅读:

  • "Go数据结构“
  • “围棋片:使用和内部”
  • 数组、切片(和字符串):“追加”的机制
  • golang-nuts --请密切关注Rob的回复。
票数 11
EN

Stack Overflow用户

发布于 2013-12-31 02:13:06

Go程序设计语言规范 呼叫 在函数调用中,函数值和参数按通常的顺序计算。在对它们进行评估之后,调用的参数将通过值传递给函数,被调用的函数开始执行。当函数返回时,函数的返回参数将通过值传递回调用函数。

在围棋中,一切都是通过价值传递的。

罗伯派克 在围棋中,一切都是通过价值传递的。所有的一切。 有一些类型(指针、通道、映射、片)具有类似引用的属性,但在这些情况下,相关的数据结构(指针、通道指针、映射头、片头)持有指向基础共享对象(指向事物、通道描述符、哈希表、数组)的指针;数据结构本身是通过值传递的。一直都是。 一直都是。 -rob

票数 2
EN

Stack Overflow用户

发布于 2021-07-05 20:03:47

我的理解是,Go以及Java和C#从来没有C++的过度复制成本,但并不解决容器的所有权转移问题。因此,仍然涉及抄袭。随着C++成为一种更多的值语义语言,引用/指针被降为i)类中的智能指针托管对象,以及( ii)依赖引用,移动语义解决了过度复制的问题。注意,这与“按值传递”无关,现在在C++中,每个人都通过引用(&)或Const引用(const &)传递对象。让我们看看这个(1):

代码语言:javascript
复制
BigObject BO(big,stuff,inside);
vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BO);

或(2)

代码语言:javascript
复制
vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BigObject(big,stuff,inside));

尽管您是通过引用向量vo传递的,但是在C++03中,向量代码中有一个副本。在第二种情况下,有一个临时对象必须被构造,然后在向量中被复制。因为它只能由向量访问,所以这是一个浪费的副本。

然而,在第一种情况下,我们的意图可能只是将BO的控制权交给矢量本身。C++17允许这样做:

(1,C++17)

代码语言:javascript
复制
vector<BigObject> vo;
vo.reserve(1000000);
vo.emplace_back(big,stuff,inside);

或(2,C++17)

代码语言:javascript
复制
vector<BigObject> vo;
vo.reserve(1000000);
vo.push_back(BigObject(big,stuff,inside));

从我所读到的情况来看,尚不清楚Java、C#或Go是否可以免于与C++03在容器中遭受的复制相同的复制。

老式的牛(复制-写)技术也有同样的问题,因为一旦向量内的对象被复制,资源就会被复制。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20849911

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档