Discussion: Rvalue refs and a Move construtor for D

kinke noone at nowhere.com
Wed Sep 4 19:01:54 UTC 2019


On Wednesday, 4 September 2019 at 18:43:08 UTC, Manu wrote:
> On Wed, Sep 4, 2019 at 7:50 AM Suleyman via Digitalmars-d 
> <digitalmars-d at puremagic.com> wrote:
>>
>> On Wednesday, 4 September 2019 at 00:58:44 UTC, Manu wrote:
>> > Move semantics aren't a feature of the move constructor, they
>> > are USED
>> > BY the move constructor.
>> > You can move an argument to any function. Consider a
>> > unique_ptr, which
>> > can only move. It would be impossible to pass a unique_ptr to
>> > any
>> > other function than the move constructor itself.
>>
>> If you can get the move constructor and move assignment in 
>> addition to an intrinsic function for moving lvalues to 
>> rvalues then you can do move semantics without rvalue ref.
>>
>> unique_ptr in C++ doesn't move itself magically. You have to
>> eplicitly move it by calling `move()`. Example:
>> https://cpp.godbolt.org/z/8jVONg.
>> You can do that with the machinery provided in the POC without
>> rvalue ref.
>
> You misunderstood what I'm saying.
> I'm saying that it can't only be a `@move` constructor that can 
> accept
> rval references, any argument should be able to be one. 
> Functions may
> have multiple arguments, and some of them may be unique_ptr-like
> objects.
> If you accept by-value, and you use a move-constructor to 
> construct
> the argument, then we're back at making a copy at every level 
> of the
> callstack, and that's specifically what we need to avoid.

Nope, Suleyman is totally right here. Move constructors are in 
fact never ever used to construct a parameter from an argument. 
The only time a move ctor is called is for `auto var = 
std::move(otherVar)`. That's how C++ handles it, and that's how 
an improved D could handle it as well, without rvalue refs in the 
language.

// high-level: by value
// low-level: just gets 2 pointers to temporaries and directly 
manipulates those,
//            no copying or moving at all
void func(T)(UniquePtr!T a, UniquePtr!T b);

void foo(T)(UniquePtr!T a) // again, `a` is a ref to a temporary
{
     // forward the `a` ref; construct a new UniquePtr and forward 
its address
     func(move(a), makeUnique!T());
}

void bar(T)()
{
     // Construct a new UniquePtr and forward its address to `foo`.
     // In the end, manipulating `a` in `func` will manipulate 
this temporary here, across 2 calls.
     foo(makeUnique!T());
}

For this to work, UniquePtr wouldn't need a move ctor. This is 
just an ABI detail (and requires a `move` intrinsic, as stated 
multiple times already).


More information about the Digitalmars-d mailing list