rvalues -> ref (yup... again!)

Atila Neves atila.neves at gmail.com
Tue Mar 27 07:33:12 UTC 2018


On Tuesday, 27 March 2018 at 00:30:24 UTC, Rubn wrote:
> On Monday, 26 March 2018 at 14:40:03 UTC, Atila Neves wrote:
>> C++ T&& (Rvalue reference) -> D T
>
> Not really, in C++ it is an actual reference and you get to 
> choose which function actually does the move. In D it just does 
> the copy when passed to the function.

It doesn't copy.

> So you can't do this in D.
>
> void bar(T&& t)
> {
>     // actually move contents of T
> }
>
> void foo(T&& t)
> {
>     bar(std::forward<T>(t)); // Can't do this in D without 
> making another actual copy cause it isn't a reference
> }


You can most definitely do this in D:

void bar(T)(auto ref T t) {
     // T is a ref for lvalues, by value for rvalues
}

void foo(T)(auto ref T t) {
     import std.functional: forward;
     bar(forward!t);
}


More to the point:

import std.stdio;

struct Foo {
     ubyte[] data;

     this(int n) {
         writeln("ctor n = ", n);
         data.length = n;
     }

     this(this) {
         writeln("postBlit n = ", data.length);
         data = data.dup;
     }
}

void foo(T)(auto ref T t) {
     import std.functional: forward;
     bar(forward!t);
}

void bar(T)(auto ref T t) {
     writeln("bar: ", t.data[0]);
}

void main() {
     bar(Foo(10));
     auto f = Foo(5);
     f.data[0] = 42;
     bar(f);
}


The output is:

ctor n = 10
bar: 0
ctor n = 5
bar: 42

Notice the lack of "postBlit" in the output. No copies were made. 
In D, by value *does not* mean copy. And given that, contrary to 
C++, the compiler doesn't write the postBlit constructor for you, 
you'd only ever get copies if you implemented it yourself!

> What's a concrete example that you would be required to know 
> whether a const& is a temporary or not.

To know whether or not you can move instead of copy. If it's a 
temporary, you can move. If it's not, you have to copy. Since 
temporaries bind to const T& in C++, you might have a temporary, 
or you might have an lvalue. Since you don't know, you have to 
copy. To support move semantics, C++ got T&&, which lvalues can't 
bind to. So if you have a T&&, you know it's about to go away and 
a move is possible.

In D, if it's ref then it can't be a temporary. If it's a value 
then it can, and it gets moved.

> I've come across a few pains of such. It make be easier to use 
> but it comes at a performance hit. In part binaries become huge 
> because of how "init" is implemented.
>
> struct StaticArray(T, size_t capacity)
> {
>     size_t length;
>     T[capacity] values;
> }
>
> Copying the above structure copies unnecessary data for any 
> move/copy operation. Eg when length = 0, it'll still copy 
> everything. This includes initialization.

This is that rare type for which moving is the same as copying. 
In that case (assuming it gets copied, see my reply to Manu), 
pass by ref. You won't be able to pass in temporaries, but I 
think that's a small price to pay for not having rvalue 
references.

In this case specifically, I don't know why you wouldn't just 
slice it when passing to functions.

Atila


More information about the Digitalmars-d mailing list