Jump to page: 1 2 3
Thread overview
Move construction from !is(T == typeof(this))
Apr 24, 2017
Manu
Apr 24, 2017
rikki cattermole
Apr 24, 2017
Manu
Apr 24, 2017
Manu
Apr 24, 2017
Manu
Apr 24, 2017
Stanislav Blinov
Apr 24, 2017
ag0aep6g
Apr 24, 2017
Stanislav Blinov
Apr 25, 2017
Manu
Apr 25, 2017
Stanislav Blinov
Apr 26, 2017
Manu
Apr 26, 2017
Stanislav Blinov
Apr 27, 2017
Stanislav Blinov
Apr 25, 2017
Jack Applegame
Apr 25, 2017
Stanislav Blinov
Apr 25, 2017
Jack Applegame
Apr 24, 2017
Stanislav Blinov
April 24, 2017
Are there any known solutions to perform efficient move construction in D?

D's pretty good at doing moves at all the right times, but with a serious limitation compared to C++ that the type must be an exact match.

Consider this C++; really bad example, but just to illustrate:

struct X { std::string s; };

struct Y {
  std::string s;

  this(const X &x)
  {
    s = s; // copy the string, expensive
  }
  this(X &&x)
  {
    s = std::move(s); // claim s from x, efficient
  }
};

Now, I'm not saying that rval references are the only solution here, just that I can overload the construction from an X for the rvalue and non-rvalue case, which is what I want...


I'm thinking in D, this *might* be possible:

struct X {}

struct Y {
  this(auto ref X x)
  {
    static if (__traits(isRef, x))
    {
      // x is lvalue, copy construct
    }
    else
    {
      // x MUST be rvalue(?), move construct
      // does this pattern require that I invalidate x the same way C++
does such that X's destructor won't clean up or crash?
    }
  }
}


Is this solid? I have a vague memory of thinking on this once and realising there was some edge case where it was logically flawed, but I can't remember clearly.

Assuming this does work, the next issue is something that mirrors std::move(), is there already such a thing?


Finally, a further problem exists with auto ref where the function must be
a template. I have cases of code-not-available lib API's where templates
are a problem.
I would prefer to overload 2 constructors for the 2 cases, than have one
template constructor and static if inside. I wonder what would happen in
this case:

struct X {}

struct Y {
  this(ref const X x)
  {
    // x is lvalue reference, copy construct
  }
  this(X x)
  {
    // x is an lvalue... which may be a copy of another lvalue. can't move
construct :/
  }
}

I guess the question in this case is how overload selection may or may not
work...
I didn't test this, but I expect it's an ambiguous call given an lvalue?

I wonder if this overload set could be made to work such that it is certain that the non-ref overload is only called with rvalues; ie, given this ambiguous call, ref is preferred for lvalues. rval can not call ref, therefore must resolve to byval.


Where is this stuff at?


- Manu


April 24, 2017
There is[0] but idk how close it is to std:move and the likes.

[0] http://dlang.org/phobos/std_algorithm_mutation.html#.move
April 24, 2017
Yeah, that's not the same thing at all.

On 24 April 2017 at 15:00, rikki cattermole via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> There is[0] but idk how close it is to std:move and the likes.
>
> [0] http://dlang.org/phobos/std_algorithm_mutation.html#.move
>


April 24, 2017
On Monday, 24 April 2017 at 05:00:13 UTC, rikki cattermole wrote:
> There is[0] but idk how close it is to std:move and the likes.
>
> [0] http://dlang.org/phobos/std_algorithm_mutation.html#.move

std::move doesn't do anything, it is just a type-cast.
April 24, 2017
On Monday, 24 April 2017 at 04:21:36 UTC, Manu wrote:
> Now, I'm not saying that rval references are the only solution here, just that I can overload the construction from an X for the rvalue and non-rvalue case, which is what I want...

What I've done in the past is simply to create a movable_ref pointer-type. AFAICT that would be similar to C++ "&&" except it isn't downgraded when used as a parameter (which is a language feature). C++ provide that downgrading so that programmers don't accidentally forward a reference as a movable reference without making it explicit.

April 24, 2017
On 24 April 2017 at 18:36, Ola Fosheim Grøstad via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Monday, 24 April 2017 at 04:21:36 UTC, Manu wrote:
>
>> Now, I'm not saying that rval references are the only solution here, just that I can overload the construction from an X for the rvalue and non-rvalue case, which is what I want...
>>
>
> What I've done in the past is simply to create a movable_ref pointer-type. AFAICT that would be similar to C++ "&&" except it isn't downgraded when used as a parameter (which is a language feature). C++ provide that downgrading so that programmers don't accidentally forward a reference as a movable reference without making it explicit.
>

I've done that too, but that's a seriously shit solution. You didn't comment on any of my actual questions ;)


April 24, 2017
On 4/24/17 12:21 AM, Manu via Digitalmars-d wrote:

> I wonder if this overload set could be made to work such that it is
> certain that the non-ref overload is only called with rvalues; ie, given
> this ambiguous call, ref is preferred for lvalues. rval can not call
> ref, therefore must resolve to byval.

AFAIK, if you have an overload that varies solely on ref, then rvalues go to the non-ref and lvalues go to the ref. If this is not the case, it's *intended* to be the case, and should be filed as a bug.

auto ref just templates that. And in your case, it's actually clearer and cleaner not to use auto ref.

Not sure if this answers your question, or if it turns out to be a viable solution.

-Steve
April 24, 2017
On Monday, 24 April 2017 at 14:00:33 UTC, Steven Schveighoffer wrote:
> On 4/24/17 12:21 AM, Manu via Digitalmars-d wrote:
>
>> I wonder if this overload set could be made to work such that it is
>> certain that the non-ref overload is only called with rvalues; ie, given
>> this ambiguous call, ref is preferred for lvalues. rval can not call
>> ref, therefore must resolve to byval.
>
> AFAIK, if you have an overload that varies solely on ref, then rvalues go to the non-ref and lvalues go to the ref. If this is not the case, it's *intended* to be the case, and should be filed as a bug.
>
> auto ref just templates that. And in your case, it's actually clearer and cleaner not to use auto ref.
>
> Not sure if this answers your question, or if it turns out to be a viable solution.
>
> -Steve

https://issues.dlang.org/show_bug.cgi?id=17346
April 24, 2017
On Monday, 24 April 2017 at 04:21:36 UTC, Manu wrote:

> struct X {}
>
> struct Y {
>   this(auto ref X x)
>   {
>     static if (__traits(isRef, x))
>     {
>       // x is lvalue, copy construct
>     }
>     else
>     {
>       // x MUST be rvalue(?), move construct
>       // does this pattern require that I invalidate x the same way C++ does such that X's destructor won't clean up or crash?

"Require" is a bit too strict. But yes, if the pattern is to assume ownership of x's contents, then you should clear it.

> Is this solid? I have a vague memory of thinking on this once and realising there was some edge case where it was logically flawed, but I can't remember clearly.
>
> Assuming this does work, the next issue is something that mirrors std::move(), is there already such a thing?

We can't truly override move semantics in D. The language went "all in" and left little leeway to us users in that regard. Interestingly enough, we *can* come close in cases such as the code below.

> struct X {}
>
> struct Y {
>   this(ref const X x)
>   {
>     // x is lvalue reference, copy construct
>   }
>   this(X x)
>   {
>     // x is an lvalue... which may be a copy of another lvalue. can't move
> construct :/

Why not? The first overload will be called with an lvalue, i.e:

X x;
Y y1 = x;

The second one will be called in all other cases:

Y y2 = X();
Y y3 = move(x); // move(x) returns X

For second overload, x will be destructed once the ctor returns. I don't see any reason why you wouldn't move it's guts out.

> I guess the question in this case is how overload selection may or may not work...
> I didn't test this, but I expect it's an ambiguous call given an lvalue?

There is no ambiguity there as far as I can tell.
Same goes for opAssign, you can have

ref opAssign(ref X x) { /*...*/ } // assign lvalue
ref opAssign(X x) { /*...*/ } assign rvalue

I guess you mean that you don't have source for X. But in that case, you won't be able to "move-construct" Y from X in C++ either. Nor would the compiler. If you at least know the exact memory layout of X, you could hack together a custom move:

this(X x)
{
    auto px = cast(XLayout*)cast(void*)&x;
    // move from px...
}

...but that's neither here nor there. The words "dangerous", "non-portable" and UB march with pitchforks right behind that pattern ;)
April 24, 2017
On Monday, 24 April 2017 at 13:18:41 UTC, Manu wrote:
> I've done that too, but that's a seriously shit solution. You didn't comment on any of my actual questions ;)

It answered your first question :)

« First   ‹ Prev
1 2 3