D's equivalent to C++'s std::move?

Ali Çehreli via Digitalmars-d digitalmars-d at puremagic.com
Tue Feb 2 14:36:22 PST 2016


On 02/01/2016 05:21 AM, Shachar Shemesh wrote:

 > I have a non-copyable struct with move semantics. In other words, a
 > struct with @disable this(this), but with working overloads for the
 > this(copy) and opAssign.
 >
 > Now I have an instance of that struct. I would like to be able to
 > voluntarily give up ownership for the sake of another instance.
 >
 > In C++, I would do something like this:
 >
 > unique_ptr<int> p (new int), q;
 >
 > q = std::move(p);
 >
 > I am unsure what is the correct way to do this under D.

This question has been brought up a lot lately. I've decided to look at 
this more seriously yesterday.

My first observation is that if @post-blit is disabled isn't the struct 
already a unique type? If so, we don't need unique_ptr for variables of 
that type, right? Am I completely off there?

vector<unique_ptr<T>> is a valid use case but it's not possible with D's 
arrays because D's arrays work with .init object, which require 
assigning into.

So, I've played with an array type that can store non-copyable types. 
The main functions are emplace_back() and move_back(). One requirement 
is that the stored type must be idempotent regarding destruction of its 
.init value.

Have you used something similar before? Is this a correct approach to 
this problem?

(Pardon the non-D naming convention that uses underscores.)

/* An array that can store non-copyable types. */
struct UniqueArray(T) {
     ubyte[] storage;
     size_t count;

     @disable this(this);

     this(size_t size) {
         storage = new ubyte[](size);
     }

     ~this() {
         foreach (i; 0 .. count) {
             destroy_at(i);
         }
     }

     /* Returns the address of element at index 'i'.*/
     T* addressOf(size_t i) {
         const offset = T.sizeof * i;
         return cast(T*)(storage.ptr + offset);
     }

     /* Returns a range to all elements. TODO: Use operator overloading. */
     auto all() {
         auto arr = (cast(T*)(storage.ptr))[0..count];
         T*[] result;
         foreach (ref i; arr) {
             result ~= &i;
         }
         return result;
     }

     import std.typecons : Flag, Yes, No;

     /* Emplaces a new object at index 'i' with the given constructor
      * arguments. */
     T* emplace_at(Flag!"occupied" occupied = Yes.occupied,
                   Args...)(size_t i, Args args) {
         import std.exception : enforce;
         import std.conv : emplace;

         enforce(i <= count);

         T* place = addressOf(i);

         /* TODO: Be exception-safe; don't destroy before succesful
          * construction. */
         if (occupied) {
             destroy_at(i);
         }

         emplace(place, args);
         return place;
     }

     /* Emplaces an object at the end with the given constructor 
arguments. */
     T* emplace_back(Args...)(Args args) {
         if (storage.length == (count * T.sizeof)) {
             storage.length += T.sizeof;
         }

         const isOccupied = false;
         T* place = emplace_at!(No.occupied)(count, args);
         ++count;
         return place;
     }

     /* Moves an lvalue to the end. The arguments becomes T.init. */
     void move_back(ref T s) {
         import std.algorithm : move;

         T* place = emplace_back();
         move(s, *place);
     }

     /* Destroys the element at index 'i'. */
     void destroy_at(size_t i) {
         destroy(*addressOf(i));
     }
}

UniqueArray!S uniqueArray(S)(size_t size = 0) {
     return UniqueArray!S(size);
}

/* Test code follows. */

import std.stdio;

/* A non-copyable type. */
struct S {
     int i = -1;

     @disable this(this);

     void printInfo(string func = __FUNCTION__)() {
         writefln("%s for %s", func, i);
     }

     this(int i) {
         this.i = i;
         printInfo();
     }

     ~this() {
         printInfo();

         if (i == -1) {
             /* This type does not do anything special for its .init 
value. */
             writefln("  ... (Skipping cleanup for .init)");

         } else {
             writefln("  Doing proper cleanup");
         }
     }
}

void main() {
     auto u = uniqueArray!S();

     /* Some are emplaced back, some are moved back. */
     foreach (i; 0 .. 5) {
         if (i % 2) {
             writefln("Emplacing rvalue %s back", i);
             u.emplace_back(i);

         } else {
             writefln("Making lvalue %s", i);
             auto s = S(i);
             writefln("Moving lvalue %s back", i);
             u.move_back(s);

             assert(s == S.init);
         }
     }

     writefln("Replacing 2 with 100");
     u.emplace_at(2, 100);

     writefln("Destroying %s", 1);
     u.destroy_at(1);

     /* Just do someting with element states. NOTE: writeln(u) does not work
      * because 'u' is not copyable. */
     foreach (i, ref e; u.all) {
         writefln("%s: %s", i, e.i);
     }

     writefln("Leaving main");
}

For convenience, here is the output of the program:

Making lvalue 0
deneme.S.this for 0
Moving lvalue 0 back
deneme.S.~this for -1
   ... (Skipping cleanup for .init)
deneme.S.~this for -1
   ... (Skipping cleanup for .init)
Emplacing rvalue 1 back
deneme.S.this for 1
Making lvalue 2
deneme.S.this for 2
Moving lvalue 2 back
deneme.S.~this for -1
   ... (Skipping cleanup for .init)
deneme.S.~this for -1
   ... (Skipping cleanup for .init)
Emplacing rvalue 3 back
deneme.S.this for 3
Making lvalue 4
deneme.S.this for 4
Moving lvalue 4 back
deneme.S.~this for -1
   ... (Skipping cleanup for .init)
deneme.S.~this for -1
   ... (Skipping cleanup for .init)
Replacing 2 with 100
deneme.S.~this for 2
   Doing proper cleanup
deneme.S.this for 100
Destroying 1
deneme.S.~this for 1
   Doing proper cleanup
0: 0
1: -1
2: 100
3: 3
4: 4
Leaving main
deneme.S.~this for 0
   Doing proper cleanup
deneme.S.~this for -1
   ... (Skipping cleanup for .init)
deneme.S.~this for 100
   Doing proper cleanup
deneme.S.~this for 3
   Doing proper cleanup
deneme.S.~this for 4
   Doing proper cleanup

Ali



More information about the Digitalmars-d mailing list