31

A problem of "value types" with external resources (like std::vector<T> or std::string) is that copying them tends to be quite expensive, and copies are created implicitly in various contexts, so this tends to be a performance concern. C++0x's answer to this problem is move semantics, which is conceptionally based on the idea of resource pilfering and technically powered by rvalue references.

Does D have anything similar to move semantics or rvalue references?

fredoverflow
  • 256,549
  • 94
  • 388
  • 662

5 Answers5

24

I believe that there are several places in D (such as returning structs) that D manages to make them moves whereas C++ would make them a copy. IIRC, the compiler will do a move rather than a copy in any case where it can determine that a copy isn't needed, so struct copying is going to happen less in D than in C++. And of course, since classes are references, they don't have the problem at all.

But regardless, copy construction already works differently in D than in C++. Generally, instead of declaring a copy constructor, you declare a postblit constructor: this(this). It does a full memcpy before this(this) is called, and you only make whatever changes are necessary to ensure that the new struct is separate from the original (such as doing a deep copy of member variables where needed), as opposed to creating an entirely new constructor that must copy everything. So, the general approach is already a bit different from C++. It's also generally agreed upon that structs should not have expensive postblit constructors - copying structs should be cheap - so it's less of an issue than it would be in C++. Objects which would be expensive to copy are generally either classes or structs with reference or COW semantics.

Containers are generally reference types (in Phobos, they're structs rather than classes, since they don't need polymorphism, but copying them does not copy their contents, so they're still reference types), so copying them around is not expensive like it would be in C++.

There may very well be cases in D where it could use something similar to a move constructor, but in general, D has been designed in such a way as to reduce the problems that C++ has with copying objects around, so it's nowhere near the problem that it is in C++.

Jonathan M Davis
  • 37,181
  • 17
  • 72
  • 102
  • Isn't postblit a D2-specific thing? I think it'd be good to clarify which version of D we're discussing now :) – Kos Nov 17 '10 at 10:37
  • 6
    @Kos I don't know what D1 does and doesn't have. I only use D2 and generally assume that a question on D relates to D2 unless stated otherwise - particularly when the question appears to be coming from someone who isn't a D user and may not even be aware that there is a version 1 and version 2 of the language. I just treat the term D as meaning the current version of the language. – Jonathan M Davis Nov 17 '10 at 17:31
  • But D2 was... well, kind of C++0x-ish last time I checked, i.e. specification changing all the time and compiler support being somewhate "experimental"? This leads me to conclusion that one shall make clear whether he talks about the stable or experimental version or the language. Or maybe D2 has gone stable finally? I'm not up-to-date with D :-) – Kos Nov 17 '10 at 18:20
  • 3
    With the release of Andrei Alexandrescu's *The D Programming Language*, D2 as a language has become essentially stable - minor changes may be made - but nothing big. The compiler still needs work (though many of the bugs apply to D1 as well), and the standard library is under heavy development, but the language spec is no longer under heavy development. This question covered the situation as of a few months ago, and the situation is still essentially the same: http://stackoverflow.com/questions/3205509/what-are-the-current-challenges-of-the-d-programming-language – Jonathan M Davis Nov 17 '10 at 19:42
  • 1
    I can't make any sense of the third paragraph in this answer. You say: *Containers are generally reference types (in Phobos, they're structs rather than classes)*. How can a struct be a reference type? Shouldn't it be the other way around? – Nordlöw Oct 31 '12 at 09:56
  • @Nordlow Structs can be reference types if copying one doesn't do a deep copy. For instance, if the struct held a pointer or class reference as a member variable, and it didn't declare a postblit constructor, then every copy of that struct would point to the same data. `std.typecons.RefCounted` would be an example of that as would any container in std.container which is a struct. – Jonathan M Davis Oct 31 '12 at 15:48
  • To expand on this answer a little, I think the fundamental difference is that D's implementation is allowed to move structs around in memory without invoking any kind of constructor; your struct simply mustn't contain a pointer to/into itself. D is also allowed to copy them, in which case it invokes postblit just so you can resolve any now-shared references inside the struct, but postblit isn't for other purposes such as fixing pointers (you don't have enough info). So, D does have both move and copy semantics; it just ALWAYS specifies the move constructor for you. :) (Correct me if I'm wrong) – entheh May 18 '13 at 20:01
  • I still don't understand paragraphs 3. Secondly, surely D's method of doing shallow copy first and then you manually adjust anything which requires deep copy is flawed in that it's inefficient? If you have a pointer to some data, and you first shallow copy it, only then to allocate memory and change it's value? There's 2 writes to a pointer there when you only needed 1? – FreelanceConsultant Aug 24 '15 at 11:20
  • @user3728501 I don't know what else to say about paragraph 3. Copying a class object is just copying a reference. So, it's basically the same as copying a pointer. Copying a struct which is a reference type (e.g. it just contains a pointer) would then be the same or perhaps slightly more expensive if it involves ref counting. D containers are reference types (be they structs or classes), so copying them around is cheap. C++ on the other hand makes it so that its containers are value types, so passing them around results in deep copies, which is expensive. – Jonathan M Davis Aug 25 '15 at 16:43
  • @user3728501 As far as postblit efficiency goes, yes, it could be slightly less efficient to do a shallow copy and then deep copy specific members, but you're talking about reassigning one pointer when you're allocating memory to do a deep copy, so that cost is dwarfed by the deep copy. The main benefit is that you don't have to copy each member explicitly like in C++. So, it reduces the amount of code and helps with correctness. The major downside is that it doesn't currently work with const or immutable, since the posblit mutates member variables. Most structs don't have postblits though. – Jonathan M Davis Aug 25 '15 at 16:48
  • @JonathanMDavis Thanks for the info... I really don't like that inefficiency though – FreelanceConsultant Aug 25 '15 at 17:49
4

I think all answers completely failed to answer the original question.

First, as stated above, the question is only relevant for structs. Classes have no meaningful move. Also stated above, for structs, a certain amount of move will happen automatically by the compiler under certain conditions.

If you wish to get control over the move operations, here's what you have to do. You can disable copying by annotating this(this) with @disable. Next, you can override C++'s constructor(constructor &&that) by defining this(Struct that). Likewise, you can override the assign with opAssign(Struct that). In both cases, you need to make sure that you destroy the values of that.

For assignment, since you also need to destroy the old value of this, the simplest way is to swap them. An implementation of C++'s unique_ptr would, therefore, look something like this:

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;
    }
}

Edit: Notice I did not define opAssign(ref UniquePtr!T that). That is the copy assignment operator, and if you try to define it, the compiler will error out because you declared, in the @disable line, that you have no such thing.

Shachar Shemesh
  • 8,193
  • 6
  • 25
  • 57
  • 1
    Your code is being discussed here in case you are interested http://forum.dlang.org/post/oeuwcairtxqbfkvxbmcg@forum.dlang.org – Maik Klein Jan 31 '16 at 20:36
1

D have separate value and object semantics :

  • if you declare your type as struct, it will have value semantic by default
  • if you declare your type as class, it will have object semantic.

Now, assuming you don't manage the memory yourself, as it's the default case in D - using a garbage collector - you have to understand that object of types declared as class are automatically pointers (or "reference" if you prefer) to the real object, not the real object itself.

So, when passing vectors around in D, what you pass is the reference/pointer. Automatically. No copy involved (other than the copy of the reference).

That's why D, C#, Java and other language don't "need" moving semantic (as most types are object semantic and are manipulated by reference, not by copy).

Maybe they could implement it, I'm not sure. But would they really get performance boost as in C++? By nature, it don't seem likely.

Klaim
  • 67,274
  • 36
  • 133
  • 188
1

I somehow have the feeling that actually the rvalue references and the whole concept of "move semantics" is a consequence that it's normal in C++ to create local, "temporary" stack objects. In D and most GC languages, it's most common to have objects on the heap, and then there's no overhead with having a temporary object copied (or moved) several times when returning it through a call stack - so there's no need for a mechanism to avoid that overhead too.

In D (and most GC languages) a class object is never copied implicitly and you're only passing the reference around most of the time, so this may mean that you don't need any rvalue references for them.

OTOH, struct objects are NOT supposed to be "handles to resources", but simple value types behaving similar to builtin types - so again, no reason for any move semantics here, IMHO.

This would yield a conclusion - D doesn't have rvalue refs because it doesn't need them.

However, I haven't used rvalue references in practice, I've only had a read on them, so I might have skipped some actual use cases of this feature. Please treat this post as a bunch of thoughts on the matter which hopefully would be helpful for you, not as a reliable judgement.

Kos
  • 70,399
  • 25
  • 169
  • 233
0

I think if you need the source to loose the resource you might be in trouble. However being GC'ed you can often avoid needing to worry about multiple owners so it might not be an issue for most cases.

BCS
  • 75,627
  • 68
  • 187
  • 294