Can you move a disabled this(this) struct in to a container type if it's an rvalue?

Stanislav Blinov stanislav.blinov at gmail.com
Thu Dec 13 23:33:39 UTC 2018


On Thursday, 13 December 2018 at 13:17:05 UTC, aliak wrote:

> Ah. Is there any case where you would not want to do that when 
> you have a T value as parameter?

Hypothetically, yes, e.g. an object that contains references to 
itself. However, D operates on the assumption that you don't have 
such objects. And even though it can't be statically checked, 
move and swap do actually perform this check at runtime in debug 
builds.
Operating under that rule, it should be legal to move any values 
that are passed to you. In fact, I postulate that it *must* be 
done instead of making copies. Unfortunately, Phobos doesn't 
agree.

struct S {
     int x;
     this(this) { x++; }
}

import std.stdio;

writeln(S.init);

I would expect that to print S(0). However, it doesn't, which is 
sad.

> And, what if it's "this()(auto ref T value)"? Then moving could 
> be dangerous if the parameter was passed as a ref. Or maybe it 
> just wouldn't compile?

In that case moving indeed could be dangerous since you'd be 
modifying caller's state. A workaround is indeed to have 
different signatures or use `auto ref`. The constructor example 
in this thread doesn't fully address the difference between 
copying and moving though, because it's dealing with 
initialization. A more practical example is an actual container, 
like an array (simplified for brevity):

struct Array(T) {
     T[] memory;
     size_t size;

     void append()(auto ref T x) {
         static if (__traits(isRef, x)) {
             import std.conv : emplace;
             // copy-construct. Note that this is different than 
doing
             // memory[size++] = x;
             // because that will call opAssign instead of 
initializing + postblit
             emplace(&memory[size++], x);
         } else {
             import std.algorithm.mutation : moveEmplace;
             moveEmplace(x, memory[size++]);
         }
     }
}

That's different from the example being discussed, since 
assignment inside a constructor gets special treatment:

struct Container(T) {
     T data;
     this()(auto ref T x) {
         static if (__traits(isRef, x))
             data = x; // this assignment is initialization, so 
emplace is not needed, copy is being made
         else
             moveEmplace(x, data);
     }
}


More information about the Digitalmars-d-learn mailing list