D有类似于C ++ 0x的移动语义吗?

时间:2010-11-16 23:45:08

标签: c++ c++11 d rvalue-reference move-semantics

外部资源(如std::vector<T>std::string)的“值类型”问题是复制它们往往非常昂贵,并且副本是在各种上下文中隐式创建的,因此这往往会是一个表现问题。 C ++ 0x对这个问题的回答是移动语义,它在概念上基于资源窃取的概念,并且由 rvalue references 技术驱动。

D是否有类似移动语义或右值引用的东西?

5 个答案:

答案 0 :(得分: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 ++中的问题。

答案 1 :(得分:1)

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

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

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

所以,当在D中传递向量时,你传递的是引用/指针。自动。不涉及任何副本(参考文献的副本除外)。

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

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

答案 2 :(得分:1)

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

在D(和大多数GC语言)中,永远不会隐式复制class对象,并且您只是在大多数时间内传递引用,所以可能意味着你不要不需要任何右值参考。

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

这会产生结论 - D没有rvalue refs,因为它不需要它们

但是,我在实践中没有使用rvalue引用,我只读过它们,所以我可能已经跳过了这个功能的一些实际用例。请把这篇文章视为一堆关于此事的想法,希望对你有所帮助,而不是作为一种可靠的判断。

答案 3 :(得分:1)

我认为所有答案都完全没有回答原来的问题。

首先,如上所述,问题仅与结构有关。课程没有任何有意义的举动。如上所述,对于结构体,编译器在某些条件下会自动进行一定量的移动。

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

答案 4 :(得分:0)

我认为如果你需要资源来放松资源,你可能会遇到麻烦。无论你是GC,都可以避免担心多个拥有者,所以在大多数情况下这可能不是问题。

相关问题