D 是否有类似于 C++0x 的移动语义的东西?
-
25-09-2019 - |
题
外部资源的“值类型”问题(例如 std::vector<T>
或者 std::string
)是复制它们往往非常昂贵,并且副本是在各种上下文中隐式创建的,因此这往往是一个性能问题。C++0x 对这个问题的答案是 移动语义, ,其概念上基于资源窃取的思想,技术上由 右值引用.
D 有类似移动语义或右值引用的东西吗?
解决方案
我认为有在d几个地方(诸如返回结构),该d管理,使它们移动,而C ++会使他们的副本。 IIRC,编译器会做的举动,而不是在它可以确定不需要拷贝任何情况下副本,因此结构复制将在d比C ++发生较小。当然,因为类是引用,他们没有问题的。
但不管,复制结构已经工作方式不同在d比在C ++。一般情况下,代替声明拷贝构造函数,你声明postblit构造:this(this)
。它this(this)
前全的memcpy被调用,你只能让任何变化是必要的,以确保新的结构是从原来的独立(如做在需要成员变量的深层副本),而不是创建一个全新的构造函数必须复制的一切。所以,一般的方法是从已有的C ++有点不同。它也普遍认为在该结构不应该有昂贵postblit构造 - 复制结构应该是廉价的 - 所以这是一个问题的不到它会在C ++。这将是昂贵的复制对象通常要么是类或结构,参照或COW语义。
集装箱通常是引用类型(在火卫一,他们是结构而不是类,因为它们不需要多态性,但将它们复制并不会复制它们的内容,所以他们仍然引用类型),所以复制身边不贵像它会在C ++。
有很可能是在d情况下,它可以使用类似的举动构造函数的东西,但总的来说,d,设计了这样一种方式,以减少问题,C ++与各地复制对象,所以它的无处近,这是在C ++中的问题。
其他提示
D 具有单独的值和对象语义:
- 如果你将你的类型声明为
struct
, ,默认有值语义 - 如果你将你的类型声明为
class
, ,它将具有对象语义。
现在,假设您不自己管理内存,因为这是 D 中的默认情况 - 使用垃圾收集器 - 您必须理解声明为的类型的对象 class
自动指向真实对象的指针(或“引用”,如果您愿意),而不是真实对象本身。
因此,当在 D 中传递向量时,您传递的是引用/指针。自动地。不涉及任何副本(除了参考文献的副本)。
这就是为什么 D、C#、Java 和其他语言“不需要”移动语义(因为大多数类型是对象语义,并且通过引用而不是通过复制进行操作)。
也许他们可以实施它,我不确定。但它们真的能像 C++ 那样获得性能提升吗?从本质上来说,这似乎不太可能。
我莫名其妙地有一种感觉,其实是右值引用和“移动语义”的整个概念是一个结果,它是用C ++正常创建本地,“临时”栈对象。的在d和最GC语言,这是最常见的,以对堆对象,再有通过一个调用返回时,它具有临时对象复制(或移动)几次没有开销堆强> - 因此有没有必要建立一种机制,以避免开销太大。
在d(最GC语言)一class
对象从不隐复制你只通过周围的大部分时间基准,所以这个的可以的意思是你不需要任何为他们rvalue引用。
OTOH,struct
对象不应该是“句柄资源”,但简单的值类型的行为类似于内建类型 - 如此反复,没有理由在这里的任何移动语义,恕我直言
这将产生一个结论 - d没有右值裁判,因为它并不需要它们
然而在实践中,我没有用右值引用,我只对他们阅读,所以我可能会跳过此功能的一些实际的使用情况。请把这个岗位作为一堆对此问题的想法希望这将是对你有所帮助,而不是作为一个可靠的判断。
我想所有的答案完全没有回答原来的问题。
首先,如上所述,问题是只对结构相关的。类没有意义的举动。上面还指出,对于结构,移动一定量的通过将在一定条件下,编译器自动发生。
如果你希望得到控制的移动操作,这里是你必须做的事情。您可以禁用通过@disable注释本(本)复制。接下来,你可以通过定义constructor(constructor &&that)
覆盖C ++的this(Struct that)
。同样,你可以重写与opAssign(Struct that)
的分配。在这两种情况下,你需要确保你摧毁that
的值。
有关任务,因为你还需要摧毁this
的旧值,最简单的方法就是交换它们。 C ++的unique_ptr
的实现会,因此,看起来像这样:
struct UniquePtr(T) {
private T* ptr = null;
@disable this(this); // This disables both copy construction and opAssign
// The obvious constructor, destructor and accessor
this(T* ptr) {
if(ptr !is null)
this.ptr = ptr;
}
~this() {
freeMemory(ptr);
}
inout(T)* get() inout {
return ptr;
}
// Move operations
this(UniquePtr!T that) {
this.ptr = that.ptr;
that.ptr = null;
}
ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that"
swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary
return this;
}
}
编辑:
请注意我没有定义opAssign(ref UniquePtr!T that)
。这是拷贝赋值运算符,如果你试图去定义它,编译器会报错,因为你宣布,在@disable
行,你有没有这样的事情。
我想,如果你需要源宽松,你可能会遇到麻烦的资源。但是作为GC'ed你常常可以避开需要约多的业主担心,因此可能不适合大多数情况下的问题。