March 26, 2018
On 26 March 2018 at 16:21, Rubn via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Monday, 26 March 2018 at 22:48:38 UTC, Walter Bright wrote:
>>
>> On 3/26/2018 12:24 PM, Manu wrote:
>>>
>>> On 26 March 2018 at 07:40, Atila Neves via Digitalmars-d
>>>>
>>>> C++ const T& -> D T
>>>
>>>
>>> Yeah, no... T may be big. Copying a large thing sucks. Memory copying
>>> is the slowest thing computers can do.
>>> As an API author, exactly as in C++, you will make a judgement on a
>>> case-by-case basis on this matter. It may be by-value, it may be by
>>> const-ref. It depends on a bunch of things, and they are points for
>>> consideration by the API author, not the user.
>>
>>
>> Copying does suck, I agree. Consider the following:
>>
>>     void foo(T t) { foo(t); } <= add this overload
>>     void foo(ref T t) { ... }
>>     T aaa();
>>
>>     foo(aaa());
>>
>> With inlining, I suspect we can get the compiler to not make any extra copies. It's not that different from NRVO. And as a marvy bonus, no weird semantic problems (as Atila mentioned).
>
>
> How do you add this overload for the following?
>
>
> void foo(ref T t) { ... }
>
> void function(ref int) func = &foo;
> int aaa();
>
> func(aaa()); // err

Exactly.
March 26, 2018
On 26 March 2018 at 19:25, Manu <turkeyman@gmail.com> wrote:
> On 26 March 2018 at 16:21, Rubn via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>> On Monday, 26 March 2018 at 22:48:38 UTC, Walter Bright wrote:
>>>
>>> On 3/26/2018 12:24 PM, Manu wrote:
>>>>
>>>> On 26 March 2018 at 07:40, Atila Neves via Digitalmars-d
>>>>>
>>>>> C++ const T& -> D T
>>>>
>>>>
>>>> Yeah, no... T may be big. Copying a large thing sucks. Memory copying
>>>> is the slowest thing computers can do.
>>>> As an API author, exactly as in C++, you will make a judgement on a
>>>> case-by-case basis on this matter. It may be by-value, it may be by
>>>> const-ref. It depends on a bunch of things, and they are points for
>>>> consideration by the API author, not the user.
>>>
>>>
>>> Copying does suck, I agree. Consider the following:
>>>
>>>     void foo(T t) { foo(t); } <= add this overload
>>>     void foo(ref T t) { ... }
>>>     T aaa();
>>>
>>>     foo(aaa());
>>>
>>> With inlining, I suspect we can get the compiler to not make any extra copies. It's not that different from NRVO. And as a marvy bonus, no weird semantic problems (as Atila mentioned).
>>
>>
>> How do you add this overload for the following?
>>
>>
>> void foo(ref T t) { ... }
>>
>> void function(ref int) func = &foo;
>> int aaa();
>>
>> func(aaa()); // err
>
> Exactly.

We're just kicking the can. And the only reason to do so is
ideological, as far as I can tell.
I want to hear an argument against... or any issue that's introduced
by allowing the implicit temporary?
March 26, 2018
On 26 March 2018 at 17:30, Rubn via Digitalmars-d <digitalmars-d@puremagic.com> 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. 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 still get there with D, if you assume the forwarding function
is simple enough to inline (and the code is public).
I was cautiously concerned about this for a long time, but I've never
made a noise about it. I've digested and resolved that's okay though
:)
There are very few cases where you will fail to achieve equivalent
efficiency, and I think D's amazingly simplified move semantics more
than compensate for any conceivable loss.


>> If replacing const T& with T chafes, I understand. I used to feel that way too. It's _possible_ that would incur a penalty in copying/moving, but IME the cost is either 0, negligible, or negative (!).
>>
>> As mentioned above, if calling C++ code there's no choice about using T instead of const T&, so for pragmatic reasons that should be allowed. But only there, because...
>>
>>> Can you please explain these 'weirdities'?
>>> What are said "major unintended consequences"?
>>
>>
>> Rvalue references. They exist because of being able to bind temporaries to const T& in C++, which means there's no way of knowing if your const T& was originally a temporary or not. To disambiguate C++11 introduced the type system horror that are rvalue references (which also do double-duty in enabling perfect forwarding!).
>
>
> What's a concrete example that you would be required to know whether a const& is a temporary or not. I don't really see it otherwise. The solution everyone is saying to use as an alternative would be the literal case of how it would be implemented in the language.

He's trying to say that C++ introduced rvalue references because normal references weren't able to allow for move semantics to exist. It's a red-herring. D already has move semantics, they work well, and they're not on trial here.

In C++'s case, it's not that references were deficient at being
references that C++ needed rval-references, it's that references were
deficient at being move-able.
That is not a problem that exists in D. It's fine for references to
just be references in D. We're not struggling to make references
move-able in D, that's not a thing, we already have move semantics.
Any extension of this conversation about references into C++
rvalue-references (T&&) and or move-semantics are red-herrings.
There's no such problem in D that needs to be resolved, and the
existing solution is excellent.
March 27, 2018
On Monday, 26 March 2018 at 19:24:13 UTC, Manu wrote:
> On 26 March 2018 at 07:40, Atila Neves via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>> On Friday, 23 March 2018 at 22:01:44 UTC, Manu wrote:
>>>
>>> Forked from the x^^y thread...
>>> <snip>
>>
>>
>> C++ T&& (forwarding reference) -> D auto ref T
>> C++ T&& (Rvalue reference) -> D T
>> C++ const T& -> D T
>
> Yeah, no... T may be big. Copying a large thing sucks. Memory copying
> is the slowest thing computers can do.

That's _if_ T is big and _if_ it even gets copied, the combination of which I think happens very rarely. When that happens I think that the temporary isn't a big deal. This code:

struct Foo { int[1024] data; }
int byValue(Foo f) { return f.data[42]; }

Generates this assembly (ldc2 -O3, clang does the same for C++):

0000000000000000 <_D3foo7byValueFSQo3FooZi>:
   0:   8b 84 24 b0 00 00 00    mov    eax,DWORD PTR [rsp+0xb0]
   7:   c3                      ret

And I wrote a type for which a move and a copy are the same on purpose: in "real life" it's more likely that the memory will be dynamically allocated, probably held in a slice, and moved instead.

Are there cases in which there will be an expensive copy? Yes, then pass by ref/pointer. But measure first, and prefer to pass by value by default.

It's different in C++ - stick a std::vector or std::string in your struct and passing by value (usually) copies the dynamically allocated memory. In D it moves.

> As an API author, exactly as in C++, you will make a judgement on a
> case-by-case basis on this matter. It may be by-value, it may be by
> const-ref. It depends on a bunch of things, and they are points for
> consideration by the API author, not the user.

You can still do that in D. There well may be a reason to pass by const ref. I'm just saying that there aren't that many, and in that in those rare cases a temporary is fine. Especially if the alternative are rvalue references.

Atila
March 27, 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
March 27, 2018
On Tuesday, 27 March 2018 at 02:41:12 UTC, Manu wrote:

> He's trying to say that C++ introduced rvalue references because normal references weren't able to allow for move semantics to exist. It's a red-herring. D already has move semantics, they work well, and they're not on trial here.
>
> In C++'s case, it's not that references were deficient at being
> references that C++ needed rval-references, it's that references were
> deficient at being move-able.

There were deficient at being moveable because temporaries can bind to const T&.

> That is not a problem that exists in D.

Because temporaries can't bind to ref const(T).

> It's fine for references to
> just be references in D. We're not struggling to make references
> move-able in D, that's not a thing, we already have move semantics.
> Any extension of this conversation about references into C++
> rvalue-references (T&&) and or move-semantics are red-herrings.
> There's no such problem in D that needs to be resolved, and the
> existing solution is excellent.

If I'm reading you correctly (which I might not), you seem to be saying that there's a way forward in which:

1) D's move semantics aren't affected
2) No rvalue references are introduced
3) Temporaries can bind to ref const(T)

I'd love to know what that would look like.

Atila

March 27, 2018
On 27 March 2018 at 00:14, Atila Neves via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Monday, 26 March 2018 at 19:24:13 UTC, Manu wrote:
>>
>> On 26 March 2018 at 07:40, Atila Neves via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>>
>>> On Friday, 23 March 2018 at 22:01:44 UTC, Manu wrote:
>>>>
>>>>
>>>> Forked from the x^^y thread...
>>>> <snip>
>>>
>>>
>>>
>>> C++ T&& (forwarding reference) -> D auto ref T
>>> C++ T&& (Rvalue reference) -> D T
>>> C++ const T& -> D T
>>
>>
>> Yeah, no... T may be big. Copying a large thing sucks. Memory copying is the slowest thing computers can do.
>
>
> That's _if_ T is big and _if_ it even gets copied,

You've just described the exact anatomy of a ref function!
You wouldn't write a function to receive T by ref UNLESS T was both
big, and the function probably won't inline (therefore definitely
copy), and that condition will be triggered by any of the list of
reasons I've said a bunch of times (extern, dll, lib, virtual, etc).
People don't just love writing ref (well, some people might), but they
use it deliberately, typically in user-facing boundary API's for these
exact reasons.


> the combination of which
> I think happens very rarely. When that happens I think that the temporary
> isn't a big deal. This code:
>
> struct Foo { int[1024] data; }
> int byValue(Foo f) { return f.data[42]; }
>
> Generates this assembly (ldc2 -O3, clang does the same for C++):
>
> 0000000000000000 <_D3foo7byValueFSQo3FooZi>:
>    0:   8b 84 24 b0 00 00 00    mov    eax,DWORD PTR [rsp+0xb0]
>    7:   c3                      ret
>
> And I wrote a type for which a move and a copy are the same on purpose: in "real life" it's more likely that the memory will be dynamically allocated, probably held in a slice, and moved instead.
>
> Are there cases in which there will be an expensive copy? Yes, then pass by ref/pointer. But measure first, and prefer to pass by value by default.

Right. I'm talking about deliberate use of ref... Or *existing*
(deliberate) use of ref, as is the case in almost all my my cases. The
code already exists.
As you assess, use of ref in D is fairly rare. I've been looking for
cases where other people are inconvenienced by this... hard to find.
I suspect there are reasons for this. One of them is that this
inconvenience suppresses it; ie, you will choose not to use a ref even
when you might prefer to. Others include the fact that extern(C++)
isn't super popular, DLL's aren't popular, closed-source distributed
code is non popular, OOP is not popular, etc.


> It's different in C++ - stick a std::vector or std::string in your struct and passing by value (usually) copies the dynamically allocated memory. In D it moves.

Only if you ARE moving, and not copying. D must deep copy too if you
actually copy.
Your example assumes C++ doesn't have a move constructor. D has
implicit move semantics, so you can only make an equivalent comparison
where C++ also defines the move constructor so the move case doesn't
pollute the ref comparison.
Also, irrespective of whether move semantics are performed (eliding
potential deep copying, as in your example), the binary memcpy still
has to be performed when handling values by-val, unless RVO (we're not
talking about return values), or inlining is able to eliminate it.
Also, we're not talking about move semantics!


>> As an API author, exactly as in C++, you will make a judgement on a case-by-case basis on this matter. It may be by-value, it may be by const-ref. It depends on a bunch of things, and they are points for consideration by the API author, not the user.
>
>
> You can still do that in D. There well may be a reason to pass by const ref. I'm just saying that there aren't that many, and in that in those rare cases a temporary is fine. Especially if the alternative are rvalue references.

We're not talking about rvalue references, we're talking about normal references >_<


>> He's trying to say that C++ introduced rvalue references because normal references weren't able to allow for move semantics to exist. It's a red-herring. D already has move semantics, they work well, and they're not on trial here.
>>
>> In C++'s case, it's not that references were deficient at being references that C++ needed rval-references, it's that references were deficient at being move-able.
>
>
> There were deficient at being moveable because temporaries can bind to const T&.

... what? That's just not true at all.
If temporaries couldn't bind to C++ ref, then you *definitely*
wouldn't be able to move it, because you can guarantee that someone
else owns the reference. You can't move references, under any
circumstances, in either language... and that's actually the whole
point of references.

rvalue references were introduced in C++ to capture the calls with
rvalues into a separate function call, exactly the same way as the
by-value overload will catch the rvalues in D (and perform an implicit
move).
It was impossible for C++ to implement D's implicit move semantics
when receiving by-value for reasons that have nothing to do with
references; C++ allows interior pointers which breaks implicit moving,
C++ can overload default constructor which also breaks implicit moving
(no implicit way to reset the state of the prior owner).
That's the reason that C++ was forced to introduce rvalue references,
rather than implement implicit move logic like in D.

It was the choice of D to ban interior pointers, and dis-allow overloading the default struct constructor which supports D's implicit move semantics. If D didn't have those 2 things, it would need rvalue-ref's the same as C++ for the same reasons. References have no interaction with move semantics.

But again, we're not talking about move semantics here, we're just talking about references ;)


>> That is not a problem that exists in D.
>
>
> Because temporaries can't bind to ref const(T).

No. This truly has nothing to do with it.
rvalues would continue to choose the by-val function, performing a
move whenever possible. All existing rules are correct as they are.


>> It's fine for references to
>> just be references in D. We're not struggling to make references
>> move-able in D, that's not a thing, we already have move semantics.
>> Any extension of this conversation about references into C++
>> rvalue-references (T&&) and or move-semantics are red-herrings.
>> There's no such problem in D that needs to be resolved, and the
>> existing solution is excellent.
>
>
> If I'm reading you correctly (which I might not), you seem to be saying that there's a way forward in which:
>
> 1) D's move semantics aren't affected
> 2) No rvalue references are introduced
> 3) Temporaries can bind to ref const(T)
>
> I'd love to know what that would look like.

That's exactly what I've been saying. For like, 9 years..
It looks like this:
https://github.com/TurkeyMan/DIPs/blob/ref_args/DIPs/DIP1xxx-rval_to_ref.md
 (contribution appreciated)

As far as I can tell, it's completely benign, it just eliminates the
annoying edge cases when interacting with functions that take
arguments by ref. There's no spill-over affect anywhere that I'm aware
of, and if you can find a single wart, I definitely want to know about
it.
I've asked so many times for a technical destruction, nobody will
present any opposition that is anything other than a rejection *in
principle*. This is a holy war, not a technical one. And as far as I
can tell, it basically only affects me, because I do so much work
against established C++ code! >_<
March 27, 2018
On Tuesday, 27 March 2018 at 07:33:12 UTC, Atila Neves wrote:
> 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.

It copies the memory, so it does 2 memcpy's in the sense where as C++ only calls its move constructor once.

>> 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!

Well for starters your code is wrong. You are calling bar() instead of foo().

D has hidden implementation details, from your perspective it looks like it isn't doing any copying from the high-level viewpoint, but from the low-level viewpoint your object has been copied (moved memory) multiple times.

struct Foo {
    ubyte[1024] data;
}

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

Foo gfoo;

void bar(T)(auto ref T t) {
    import std.algorithm.mutation : move;
    move(t, gfoo);
}

void main() {
    foo(Foo(10));
}

_D7example__T3fooTSQr3FooZQnFNbNiNfQrZv:
  push rbp
  mov rbp, rsp
  sub rsp, 3104
  lea rax, [rbp + 16]
  lea rdi, [rbp - 2048]
  lea rcx, [rbp - 1024]
  mov edx, 1024
  mov rsi, rcx
  mov qword ptr [rbp - 2056], rdi
  mov rdi, rsi
  mov rsi, rax
  mov qword ptr [rbp - 2064], rcx
  call memcpy@PLT    <--------------------- hidden copy


Then the other copy is in move() to gfoo. That hidden copy will happen for every additional function call you try to pass Foo through.


>> 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.

D already has move semantics, an easy solution to this is to just use another keyword. It doesn't have to bind to const ref to get what is desired:

// what was suggested in the original DIP, since scope is being used for something else now
void foo(@temp ref value)
{
}

Now you don't have this problem. You only get this behavior when you basically say you don't care whether it is a temporary or not.

So what's your problem with it now ?

>> 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

This wasn't an example for rvalue references. This was an example illustrating the negative results of the current "pain-free" system. I have a 100 mb binary, 90 mb of that come from a single structure. I mean sure you don't really have to worry about implementing move constructors and such but it is far from being pain free, that's what that was suppose to illustrate.

March 27, 2018
On Tuesday, 27 March 2018 at 18:14:18 UTC, Manu wrote:
> That's exactly what I've been saying. For like, 9 years..
> It looks like this:
> https://github.com/TurkeyMan/DIPs/blob/ref_args/DIPs/DIP1xxx-rval_to_ref.md
>  (contribution appreciated)

I've followed this thread since it was made as this has been one of the very few disappointments of the language for me. I only tend to write game code and use D for hobbyist projects whilst using C++ full-time as a junior at a small games company. Even if I take the attitude that I should use D as it is intended, instead of trying to write C++ by using D, it always felt unnecessarily obstructive to require me to make a temporary variable to avoid copying something simple like a vector or a matrix. It feels very restrictive when trying to express mathematical calculations in a concise manner.

Thanks for writing that DIP, you have covered everything I would love to see in great detail with good examples! I honestly couldn't think of anything more that could be added.
March 27, 2018
On Tuesday, 27 March 2018 at 15:50:37 UTC, Atila Neves wrote:
>> It's fine for references to
>> just be references in D. We're not struggling to make references
>> move-able in D, that's not a thing, we already have move semantics.
>> Any extension of this conversation about references into C++
>> rvalue-references (T&&) and or move-semantics are red-herrings.
>> There's no such problem in D that needs to be resolved, and the
>> existing solution is excellent.
>
> If I'm reading you correctly (which I might not), you seem to be saying that there's a way forward in which:
>
> 1) D's move semantics aren't affected
> 2) No rvalue references are introduced
> 3) Temporaries can bind to ref const(T)
>
> I'd love to know what that would look like.
>
> Atila


Well currently if you only have this implemented:

void foo(const ref Type);

Type temp = Type(10);
foo(temp);

Where the hell are you going to do your move semantics? You can't do it anyways currently, it's completely meaningless cause you can't.

void foo(Type);
void foo(const ref Type);

foo(Type(10));

Now we have move semantics with an additional definition.


With the proposed change, nothing about that would change. A temporary is only passed to a const ref as a "last resort". If it can do a move instead, it will do the move. The only change that is desired is to make the first sample code above have nicer syntax. That's it, like in the first example you don't care about it being a temporary or not.