D有移动构造函数吗?

时间:2016-01-31 22:43:40

标签: d

我引用了这个SO答案Does D have something akin to C++0x's move semantics?

  

接下来,您可以通过定义此(Struct that)来覆盖C ++的构造函数(构造函数&& that)。同样,您可以使用opAssign(Struct that)覆盖赋值。在这两种情况下,您都需要确保销毁它的值。

他给出了一个这样的例子:

url('foo', [333, 'bar', 444, 'show']);
// site.com/foo/333/bar/444/show

变量// Move operations this(UniquePtr!T that) { this.ptr = that.ptr; that.ptr = null; } 总是会被移动吗?或者在某些情况下可能会复制变量that

如果我只在临时副本上取消ptr,那将是不幸的。

3 个答案:

答案 0 :(得分:4)

嗯,你也可以看看这个问题:

Questions about postblit and move semantics

在D中复制结构的方式是它的内存是blitted,然后如果它有一个postblit构造函数,则调用它的postblit构造函数。如果编译器确定副本实际上不是必需的,那么它将不会调用postblit构造函数,也不会调用原始对象上的析构函数。因此,它将移动对象而不是复制它。

特别是,根据TDPL(第251页),语言保证

  
      
  • 移动所有匿名rvalues,而不是复制。致电this(this)   当源是匿名右值时(即,a。),它永远不会被插入   临时的,如上面函数hun中所述。)
  •   
  • 在函数内部堆栈分配的所有命名临时对象   然后给elide打电话this(this)
  •   
  • 无法保证可以观察到其他潜在的分歧。
  •   

因此,在其他情况下,编译器可能会或可能不会删除副本,具体取决于当前的编译器实现和优化级别(例如,如果将左值传递给按值获取的函数,并且该变量永远不会再次引用在函数调用之后)。

所以,如果你有

void foo(Bar bar)
{}

然后foo的参数是否被移动取决于它是左值还是右值。如果它是一个右值,它将被移动,而如果它是一个左值,它可能不会(但可能取决于调用代码和编译器)。

所以,如果你有

void foo(UniquePtr!T ptr)
{}
如果ptr传递了右值,则会移动

foo,并且可能会或可能不会移动

UniquePtr传递左值(尽管通常不是)。那么,UniquePtr的内部结构会发生什么取决于你如何实现它。如果this(UniquePtr!T that) { this.ptr = that.ptr; that.ptr = null; } 禁用postblit构造函数以使其无法复制,则传递rvalue将移动参数,并传递左值将导致编译错误(因为保证rvalue被移动,而左值则不是。

现在,你拥有的是

UniquePtr

似乎与当前类型的行为具有与其参数相同的成员。所以,我假设你实际上在这里尝试做的是UniquePtr!T的复制构造函数/移动构造函数,而不是采用this(this)的任意类型的构造函数。如果这就是您正在做的事情,那么您需要一个postblit构造函数 - this(this) { // Do any deep copying you want here. e.g. arr = arr.dup; } - 而不是一个与结构本身采用相同类型的构造函数(因为D没有复制构造函数)。所以,如果您想要的是复制构造函数,那么您可以执行类似

的操作
@disable this(this);

但是如果你的struct元素的按位副本适合你的类型,那么你就不需要postblit构造函数了。但是移动是内置的,所以你不需要声明移动构造函数(移动只会使结构的成员搞砸)。相反,如果您想要保证对象被移动并且从不复制,那么您要做的就是禁用struct的postblit构造函数。 e.g。

UniquePtr!T

然后,在任何地方传递opAssign的任何时候,它都会被保证是移动或编译错误。虽然我可能认为您可能需要单独禁用opOpAssign以禁用分配,从它的外观(基于我刚刚测试的代码),您甚至不必单独禁用分配。禁用postblit构造函数也会禁用赋值运算符。但如果情况并非如此,那么您也必须禁用{{1}}。

答案 1 :(得分:3)

JMD答案涵盖了移动语义的理论部分,我可以通过一个非常简化的示例实现扩展它:

struct UniquePtr(T)
{
    private T* ptr;

    @disable this(this);

    UniquePtr release()
    {
        scope(exit) this.ptr = null;
        return UniquePtr(this.ptr);
    }
}

// some function that takes argument by value:
void foo ( UniquePtr!int ) { }

auto p = UniquePtr!int(new int);
// won't compile, postblit constructor is disabled
foo(p);
// ok, release() returns a new rvalue which is
// guaranteed to be moved without copying
foo(p.release());
// release also resets previous pointer:
assert(p.ptr is null);

答案 2 :(得分:1)

我想我可以自己回答。引用"编程语言":

  

移动所有匿名rvalues,而不是复制。永远不会插入对此(此)的调用   当来源是一个匿名的左值(即一个临时的,如   上面的函数)。

如果我理解正确,这意味着this(Struct that)永远不会是副本,因为它首先只接受rvalues。