这来自Bjarne Stroustrup的The C++ Programming Language,Fourth Edition 3.3.2.
我们真的不想要副本; 我们只是想从函数中得到结果:我们想要移动Vector而不是复制它.幸运的是,我们可以说明这个意图:
class Vector { // ... Vector(const Vector& a); // copy constructor Vector& operator=(const Vector& a); // copy assignment Vector(Vector&& a); // move constructor Vector& operator=(Vector&& a); // move assignment };鉴于该定义,编译器将选择移动构造函数来实现函数返回值的传递. 这意味着r = x + y + z将不涉及向量的复制.相反,Vector只是被移动.通常,Vector的移动构造函数很容易定义......
我知道Golang支持传统的值传递,并使用Go样式指针传递引用.
如上面的Stroustrup所描述的那样,Go是否像C++ 11一样支持"移动语义",以避免来回无用的复制?如果是这样,这是自动的,还是要求我们在代码中做一些事情来实现它.
注意:已经发布了一些答案 - 我必须消化它们,所以我还没有接受一个 - 谢谢.
细分如下:
Go中的所有内容都按值传递.
但是有三种内置的"引用类型"也是按值传递的,但在内部它们包含对单独维护的数据结构的引用:映射,切片,通道.
你自己的回答@Vector是不正确的,Go 中没有任何内容通过引用传递.相反,存在具有引用语义的类型. 它们的值仍然按值传递(sic!).
你的困惑源于你的思想目前主要由C++,Java等负担,而Go中的这些东西主要是"在C中".
以数组和切片为例.阵列是通过在Go值传递,但一个片是含有一个指针(到下面的阵列)和两个平台尺寸的整数(长度和切片的容量)的填充结构体,和它的值这个结构,其被复制 - 一个指针和两个整数 - 当它被分配或返回时等.如果你复制一个"裸"数组,它将被字面复制 - 包含它的所有元素.
这同样适用于渠道和地图.您可以考虑定义通道和地图的类型,如下所示:
type Map struct { impl *mapImplementation } type Slice struct { impl *sliceImplementation }
(顺便说一下,如果你知道C++
,你应该知道一些C++
代码使用这个技巧来降低细节暴露到头文件中.)
所以当你以后有
m := make(map[int]string)
你可以把它想象成m
有类型Map
,所以当你以后做
x := m
值的m
获取复制,但它仅包含一个指针,并且因此两个x
和m
现在引用相同的底层数据结构.是m
通过引用复制("移动语义")?当然不是!类型map和slice和channel的值是否有引用semantincs?是!
请注意,这三种类型并不特别:通过在其中嵌入指向某些复杂数据结构的指针来实现自定义类型是一种相当常见的模式.
换句话说,Go允许程序员决定他们想要的类型语义.并且Go碰巧有三种内置类型,它们已经具有引用语义(而所有其他内置类型都具有值语义).在另一个上挑选一个语义不会影响以任何方式按值复制所有内容的规则.例如,可以指向Go中任何类型的值的指针,并分配它们(只要它们具有兼容的类型) - 这些指针将按值复制.
另一个看待这个问题的角度是许多Go包(标准和第三方)更喜欢使用指向(复杂)值的指针.一个例子是os.Open()
(它在文件系统上打开一个文件)返回一个类型的值*os.File
.也就是说,它返回一个指针并期望调用代码传递这个指针.当然,围棋作者可能都宣称os.File
是一个struct
包含一个指针,从根本上让这个价值有引用语义,但他们没有这样做.我认为这样做的原因是没有特殊的语法来处理这种类型的值,所以没有理由让它们作为地图,通道和切片工作.换句话说,KISS.
推荐阅读:
"走数据结构"
"去切片:用法和内部"
数组,切片(和字符串):'追加'的机制"
一个问题golang-nuts
- 密切关注Rob Pike的回复.