On 25 April 2017 at 14:17, Stanislav Blinov via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
On Tuesday, 25 April 2017 at 01:59:55 UTC, Manu wrote:

Ah crap, I somehow missed the single-argument move() function.

Okay, so this pattern is definitely reliable? I haven't seen it clearly documented anywhere that this is the prescribed pattern, and it's come up on stack overflow a few times, but it hasn't been answered correctly.
I think this is poorly distributed knowledge.

It is definitely reliable.

As Schveighoffer pointed out, this pattern requires *another* overload for `ref const X`? Surely if there is no `ref X` and only a `ref const X` available, it should choose that one rather than the byval one when the argument is not const (as demonstrated in the bug report?)

No, the bug report is incorrect. See http://dlang.org/spec/function.html#function-overloading. Remember that, unlike C++'s '&', "ref" is not a type, it's a parameter storage class. So, given the overloads

foo(const ref X);
foo(X x);

or, more precisely:

foo(ref const(X));
foo(X x);

if you call foo with a non-const X lvalue *or* rvalue, the second overload will be chosen (the exact match):

X x;
foo(x);   // typeof(x) == X, so foo(X) is chosen. It's passed by value, so x is copied first.
foo(X()); // also calls foo(X), this time no copy is needed

const X cx;
foo(cx); // calls foo(ref const(X)), since typeof(cx) == const(X)

In C++, those foos would be ambiguous. In D, they aren't, because it first checks whether it's const or not, and then whether it's an lvalue or an rvalue.

It's demonstrated that `ref const X` might inhibit an efficient copy constructor implementation in some cases, but you can't have false-move's occurring when the lvalue is not const.

It's not about efficiency, it's about preserving type information. Once you slap "const" on, there's no getting rid of it without violating the type system.
And there won't be any "false" moves. You either have a reference to copy from, or a full-blown copy/rvalue to move from.

Right, yeah I see. So, basically, you admit that it is required to have 3 overloads; foo(X), foo(ref X) and foo(ref const X), in the event that I want to avoid needlessly copying X prior to constructing from it in the non-const case...
I can imagine in some cases, copy constructing from X, and making a copy of X then move constructing from X are similar/same cost... so it's really just a gotcha that authors need to be aware of.

I feel this is complicated and there are a lot of particulars. This needs to be documented clearly in the language docs, and there should probably be some examples how it relates to C++'s copy/move construction offerings, because it's significantly different and anyone with C++ experience will fail to get this right.