代码之家  ›  专栏  ›  技术社区  ›  fredoverflow

D是否有类似于C++ 0x的移动语义的东西?

  •  28
  • fredoverflow  · 技术社区  · 14 年前

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

    D是否有类似于移动语义或rvalue引用的内容?

    5 回复  |  直到 14 年前
        1
  •  25
  •   Jonathan M Davis    14 年前

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

    但是不管怎样,复制构造在D中的工作方式不同于C++。一般来说,不是声明复制构造函数,而是声明postblit构造函数: this(this) . 它以前做过完整的记忆 这个(这个) 被调用,您只需进行任何必要的更改,以确保新结构与原始结构分离(例如在需要时对成员变量进行深度复制),而不是创建一个必须复制所有内容的全新构造函数。因此,一般的方法已经有点不同于C++。还普遍认为,结构不应该有昂贵的ButBLIT构造函数-复制结构应该是便宜的-所以它的问题比它在C++中要少。复制代价高昂的对象通常是带有引用或COW语义的类或结构。

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

    在D中,可以使用类似于移动构造函数的一些情况,但是一般来说,D是这样设计的,以减少C++在复制对象时所遇到的问题,因此它不存在C++中的问题。

        2
  •  1
  •   Klaim    14 年前

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

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

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

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

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

    也许他们能实现,我不确定。但是他们真的能像C++一样获得性能提升吗?从本质上看,这似乎不太可能。

        3
  •  1
  •   Kos    14 年前

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

    在D(和大多数GC语言)中 class 对象从未被隐式复制,而且大多数情况下只传递引用,因此 可以 意思是你不需要任何rvalue引用。

    奥托, struct 对象不应该是“资源句柄”,但是简单的值类型的行为类似于内置类型——所以这里没有任何移动语义的理由,IMHO。

    这将得出结论-D没有rvalue ref,因为它不需要它们 .

    但是,我在实践中没有使用rvalue引用,我只阅读过它们,所以我可能跳过了这个特性的一些实际用例。请把这篇文章看作是对这件事的一堆想法,希望对你有帮助,而不是一个可靠的判断。

        4
  •  1
  •   Shachar Shemesh    9 年前

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

    首先,如上所述,这个问题只与结构相关。类没有有意义的移动。如上所述,对于结构,编译器在某些条件下会自动执行一定量的移动。

    如果你想控制移动操作,这里是你必须做的。您可以用@disable注释这个(this)来禁用复制。接下来,可以重写C++的 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 线,你没有这样的东西。

        5
  •  0
  •   BCS    14 年前

    我想如果你需要资源来释放资源,你可能会有麻烦。然而,作为GC'ed,您通常可以避免担心多个所有者,因此在大多数情况下这可能不是问题。