29

std::vector<T>具有外部资源(如or )的“值类型”的一个问题std::string是复制它们往往非常昂贵,并且副本是在各种上下文中隐式创建的,因此这往往是性能问题。C++0x 对这个问题的回答是移动语义,它在概念上基于资源盗窃的思想,并在技术上由右值引用提供支持。

D 有什么类似于移动语义或右值引用的东西吗?

4

5 回答 5

25

我相信 D 中有几个地方(例如返回结构),D 设法使它们移动,而 C++ 会使它们成为副本。IIRC,编译器在任何情况下都会进行移动而不是复制,因为它可以确定不需要复制,因此结构复制在 D 中比在 C++ 中发生的更少。当然,由于类是引用,所以它们根本没有问题。

但无论如何,复制构造在 D 中的工作方式与在 C++ 中的工作方式不同。通常,不是声明一个复制构造函数,而是声明一个 postblit 构造函数this(this):它在this(this)调用之前执行完整的 memcpy,并且您只需进行任何必要的更改以确保新结构与原始结构分开(例如在需要时对成员变量进行深层复制),而不是创建一个全新的构造函数那必须复制一切。所以,一般的方法已经和 C++ 有点不同了。人们还普遍认为,结构不应该有昂贵的 postblit 构造函数——复制结构应该很便宜——所以它比 C++ 中的问题要少。复制成本高昂的对象通常是具有引用或 COW 语义的类或结构。

容器通常是引用类型(在 Phobos 中,它们是结构而不是类,因为它们不需要多态性,但是复制它们并不会复制它们的内容,因此它们仍然是引用类型),因此复制它们并不昂贵就像在 C++ 中一样。

在 D 中很可能有一些情况可以使用类似于移动构造函数的东西,但总的来说,D 的设计方式是减少 C++ 在复制对象时遇到的问题,所以它离问题还很远它在 C++ 中。

于 2010-11-17T01:36:56.933 回答
3

我认为所有答案都完全无法回答原始问题。

首先,如上所述,该问题仅与结构有关。类没有有意义的移动。如上所述,对于结构,编译器会在特定条件下自动发生一定量的移动。

如果您希望控制移动操作,请执行以下操作。您可以通过使用 @disable 注释 this(this) 来禁用复制。接下来,您可以constructor(constructor &&that)通过定义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行中声明您没有这样的东西。

于 2016-01-31T14:29:06.393 回答
1

我有一种感觉,实际上右值引用和“移动语义”的整个概念是在 C++ 中创建本地“临时”堆栈对象是正常的结果。在 D 和大多数 GC 语言中,最常见的是堆上的对象,然后通过调用堆栈返回临时对象多次复制(或移动)没有开销- 所以不需要一种机制来也避免这种开销。

在 D(和大多数 GC 语言)中,class对象永远不会被隐式复制,并且大多数时候您只是在传递引用,因此这可能意味着您不需要它们的任何右值引用。

OTOH,struct对象不应该是“资源句柄”,而是行为类似于内置类型的简单值类型 - 所以再次,恕我直言,这里没有任何移动语义的理由。

这将得出一个结论 - D 没有右值引用,因为它不需要它们

但是,我在实践中没有使用过右值引用,我只是读过它们,所以我可能跳过了这个特性的一些实际用例。请将此帖子视为对此事的一堆想法,希望对您有所帮助,而不是作为可靠的判断。

于 2010-11-17T00:01:14.927 回答
1

D 具有单独的值和对象语义:

  • 如果您将类型声明为struct,则默认情况下它将具有值语义
  • 如果您将类型声明为class,它将具有对象语义。

现在,假设您自己不管理内存,因为它是 D 中的默认情况 - 使用垃圾收集器 - 您必须了解声明为的类型的对象class是自动指针(或“引用”,如果您愿意)对象,而不是真实对象本身。

因此,当在 D 中传递向量时,传递的是引用/指针。自动地。不涉及副本(参考副本除外)。

这就是为什么 D、C#、Java 和其他语言不需要“移动语义”的原因(因为大多数类型是对象语义并且通过引用而不是通过复制来操作)。

也许他们可以实施它,我不确定。但是他们真的会像在 C++ 中那样获得性能提升吗?从本质上讲,这似乎不太可能。

于 2010-11-16T23:51:56.120 回答
0

我认为如果您需要源来释放资源,您可能会遇到麻烦。但是,被 GC 处理后,您通常可以避免担心多个所有者,因此在大多数情况下这可能不是问题。

于 2010-11-17T00:57:22.160 回答