March 23, 2018
On Friday, March 23, 2018 17:20:09 Manu via Digitalmars-d wrote:
> On 23 March 2018 at 16:58, Jonathan M Davis via Digitalmars-d
>
> <digitalmars-d@puremagic.com> wrote:
> > On Friday, March 23, 2018 23:35:29 MattCoder via Digitalmars-d wrote:
> >> Well, to be honest I still can't understand why would you want to pass a RValue as reference.
> >
> > Well, it's frequently the case that you don't want to copy an object if you don't have to - especially in the gaming world, where every ounce of performance matters. If a function accepts an argument by ref, then no copy is made, but then you have to pass an lvalue, making it really annoying to call the function when what you have is an rvalue. On the other hand, if the function doesn't accept its argument by ref, then you can pass an rvalue (and it will be moved, making that efficient), but then lvalues get copied when that's often not what you want. C++'s solution to this was rvalue references.
>
> Ummm... rvalue-references are something completely different.
> rval-ref's are C++'s solution to move semantics.
> C++ just simply accepts rvalues passed to const& args. It makes a temp
> and passes the ref, as you expect.

It was my understanding that that _was_ an rvalue reference, and I remember that Andrei was against const ref accepting rvalues in D due to issues with rvalue references. It's quite possible that I misremember though, and I certainly am not an expert on all of the issues with rvalues and references in C++, and my C++ knowledge is getting increasingly rusty, since I don't use C++ much anymore. In any case, I have a terrible time remembering Andrei's exact arguments, but he feels very strongly about them, so anyone looking to convince him is going to have a hard time of it.

My biggest concern in all of this is that I don't want to see ref start accepting rvalues as has been occasionally discussed. It needs to be clear when a function is accept an argument by ref because it's going to mutate the object and when it's accepting by ref because it wants to avoid a copy. The addition of const solves that problem for C++, but given how restrictive const is in D, I doubt that much of anyone would ultimately be very happy with using const ref in their code very often.

> > In many cases, it works great, whereas in others,
> > it doesn't work at all (e.g. virtual functions), and it can result in
> > template bloat.
>
> And templates, that's another case where it fails.
> auto-ref is something else unrelated to this topic, and it's useful in
> a different set of cases for a different set of uses/reasons. It's got
> nothing to do with this.

auto ref has everything to do with a function accepting both rvalues and lvalues without making a copy. auto ref is the solution that was introduced into D to solve that very problem. It's just that it has limitations that make it inappropriate in a number of cases, so you don't consider it a solution to your problem.

> > So, C++ gives you control over which you do, and it's not
> > necessarily straightforward as to which you should use (though plenty of
> > older C++ programmers likely just use const& all over the place out of
> > habit).
>
> D gives you the same set of options; except that passing args by ref is a PITA in D, and ruins your code.

Which is why I said that D doesn't give you the same set of options.

- Jonathan M Davis

March 23, 2018
On 23 March 2018 at 17:58, Jonathan M Davis via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Friday, March 23, 2018 17:35:11 Manu via Digitalmars-d wrote:
>> > but that by itself isn't
>> > enough if you want it to be clear whether a function is supposed to be
>> > mutating the argument
>>
>> Functions that receive const args make it pretty clear that they don't intend to mutate the arg.
>
> Yes, but with how restrictive const is in D, I have a very hard time believing that it's going to work well to start using const ref much even if it accepted rvalues.

This is an interesting point, but I don't think it changes the balance
in any way. Thinking of the majority of my anecdotal cases, I don't
think it would be a problem.
Something complex enough for const to be a problem likely doesn't
conform to this pattern.

Further, extern(C++) functions that receive const& args are 'const
ref' regardless of D's const semantics. So for extern(C++), which I
suspect is a high percentage of cases where this issue is significant
(because D doesn't naturally follow C++'s pattern so much anyway),
that point doesn't matter.
March 23, 2018
On 23 March 2018 at 18:06, Jonathan M Davis via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Friday, March 23, 2018 17:20:09 Manu via Digitalmars-d wrote:
>> On 23 March 2018 at 16:58, Jonathan M Davis via Digitalmars-d
>>
>> <digitalmars-d@puremagic.com> wrote:
>> > On Friday, March 23, 2018 23:35:29 MattCoder via Digitalmars-d wrote:
>> >> Well, to be honest I still can't understand why would you want to pass a RValue as reference.
>> >
>> > Well, it's frequently the case that you don't want to copy an object if you don't have to - especially in the gaming world, where every ounce of performance matters. If a function accepts an argument by ref, then no copy is made, but then you have to pass an lvalue, making it really annoying to call the function when what you have is an rvalue. On the other hand, if the function doesn't accept its argument by ref, then you can pass an rvalue (and it will be moved, making that efficient), but then lvalues get copied when that's often not what you want. C++'s solution to this was rvalue references.
>>
>> Ummm... rvalue-references are something completely different.
>> rval-ref's are C++'s solution to move semantics.
>> C++ just simply accepts rvalues passed to const& args. It makes a temp
>> and passes the ref, as you expect.
>
> It was my understanding that that _was_ an rvalue reference, and I remember that Andrei was against const ref accepting rvalues in D due to issues with rvalue references.

C++ const& to 'rvalues' are just lvalue refs to temporaries, exactly
as I propose here.
rval-ref's are something completely different (all about move
semantics), and have nothing to do with this conversation.

There is a potentially interesting parallel conversation which discusses how to interact with extern(C++) functions that receive rvalue ref's, but that actually is a complex conversation, and no simple answers exist.


> In any case, I have a terrible time remembering
> Andrei's exact arguments, but he feels very strongly about them, so anyone
> looking to convince him is going to have a hard time of it.

Fortunately, I'm not trying to make any sort of argument for rvalue-ref's in D.


> My biggest concern in all of this is that I don't want to see ref start accepting rvalues as has been occasionally discussed. It needs to be clear when a function is accept an argument by ref because it's going to mutate the object and when it's accepting by ref because it wants to avoid a copy.

It's not going to mutate the argument, because it's const.


> The addition of const solves that problem for C++, but given how restrictive const is in D, I doubt that much of anyone would ultimately be very happy with using const ref in their code very often.

I expect I'll be 100% satisfied. And if not, it'll be something very
close to 100%.
This pattern is quite unlikely to proliferate within D code natively
(because auto ref, D move semantics, classes-as-ref-types, and such),
but it's essential for interacting with C++.


> auto ref has everything to do with a function accepting both rvalues and lvalues without making a copy. auto ref is the solution that was introduced into D to solve that very problem.

Yeah, somehow that emerged from this conversation years ago. I
aggressively expressed at the time that I never accepted it as a
solution, because it's not.
It's got nothing to do with this issue, and I said at the time that
I'll be very annoyed if it starts getting raised in this context ;)


> It's just that it has limitations that
> make it inappropriate in a number of cases, so you don't consider it a
> solution to your problem.

It's orthogonal to this conversation. It's the ability for templates
to automate the ref-ness of args for calling efficiency.
It's something like scott myers 'universal references'; ie,
`template<typename T> void func(T&& arg)`. It's really got nothing to
do with this conversation.


>> > So, C++ gives you control over which you do, and it's not
>> > necessarily straightforward as to which you should use (though plenty of
>> > older C++ programmers likely just use const& all over the place out of
>> > habit).
>>
>> D gives you the same set of options; except that passing args by ref is a PITA in D, and ruins your code.
>
> Which is why I said that D doesn't give you the same set of options.

It does though; D just forces you to write the temps that should be implicit by hand. There's no reason for this. It just makes code ugly, people angry, and there is no advantage.
March 24, 2018
On 24.03.2018 01:35, Manu wrote:
> Okay, let's read 'const ref' every time I say 'ref'. I thought that
> would be fairly safe to assume. Sorry!

Absolutely not. It makes absolutely no sense to restrict rvalue references to const objects. (Recall that const is transitive and actually prevents mutation. This is not C++.)
March 23, 2018
On 23 March 2018 at 20:03, Timon Gehr via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 24.03.2018 01:35, Manu wrote:
>>
>> Okay, let's read 'const ref' every time I say 'ref'. I thought that would be fairly safe to assume. Sorry!
>
>
> Absolutely not. It makes absolutely no sense to restrict rvalue references to const objects. (Recall that const is transitive and actually prevents mutation. This is not C++.)

We're not talking about rvalue-references... were talking about not-rvalue-references to temporaries.
March 24, 2018
On 24.03.2018 02:16, Manu wrote:
> This is an interesting point, but I don't think it changes the balance
> in any way. Thinking of the majority of my anecdotal cases, I don't
> think it would be a problem.
> Something complex enough for const to be a problem likely doesn't
> conform to this pattern.

Why aim for "it often works", when we want "it always works"? Forcing const upon people who want to pass rvalues by reference is just not good enough. It is bad language design.

Also I think the point about documenting mutation intent is moot, as rvalues can be receivers for method calls, which will _already_ pass an rvalue by reference no matter whether it intends to mutate it or not. We can require some special annotation for this behavior, but I'd be perfectly fine without it.
March 24, 2018
On 24.03.2018 04:14, Manu wrote:
> On 23 March 2018 at 20:03, Timon Gehr via Digitalmars-d
> <digitalmars-d@puremagic.com> wrote:
>> On 24.03.2018 01:35, Manu wrote:
>>>
>>> Okay, let's read 'const ref' every time I say 'ref'. I thought that
>>> would be fairly safe to assume. Sorry!
>>
>>
>> Absolutely not. It makes absolutely no sense to restrict rvalue references
>> to const objects. (Recall that const is transitive and actually prevents
>> mutation. This is not C++.)
> 
> We're not talking about rvalue-references... were talking about
> not-rvalue-references to temporaries.
> 

Let me rephrase:

Absolutely not. It makes absolutely no sense to force const for not-rvalue-references to temporaries. (Recall that const is transitive and actually prevents mutation. This is not C++.)
March 23, 2018
On 23 March 2018 at 20:17, Timon Gehr via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 24.03.2018 02:16, Manu wrote:
>>
>> This is an interesting point, but I don't think it changes the balance
>> in any way. Thinking of the majority of my anecdotal cases, I don't
>> think it would be a problem.
>> Something complex enough for const to be a problem likely doesn't
>> conform to this pattern.
>
>
> Why aim for "it often works", when we want "it always works"? Forcing const upon people who want to pass rvalues by reference is just not good enough. It is bad language design.

I think you need to re-read the whole thread, and understand what
we're even talking about.
Nobody wants to pass rvalues by mutable-ref... that's completely
pointless, since it's an rvalue that will timeout immediately anyway.
Passing by const-ref is perfectly acceptable.

I suspect Jonathan's talking about classic D situations with const
like, I might pass by const-ref, but then I can't call a getter that
caches the result.
That's a classic problem with D's const, and that's not on debate
here. I don't think that has any impact on this proposal; people
understand what const means in D, and that's no different here than
anywhere else.


> Also I think the point about documenting mutation intent is moot, as rvalues can be receivers for method calls, which will _already_ pass an rvalue by reference no matter whether it intends to mutate it or not. We can require some special annotation for this behavior, but I'd be perfectly fine without it.

I have no idea what this paragraph means... can you elaborate further what you're talking about?
March 23, 2018
On 23 March 2018 at 20:19, Timon Gehr via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 24.03.2018 04:14, Manu wrote:
>>
>> On 23 March 2018 at 20:03, Timon Gehr via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>>
>>> On 24.03.2018 01:35, Manu wrote:
>>>>
>>>>
>>>> Okay, let's read 'const ref' every time I say 'ref'. I thought that would be fairly safe to assume. Sorry!
>>>
>>>
>>>
>>> Absolutely not. It makes absolutely no sense to restrict rvalue
>>> references
>>> to const objects. (Recall that const is transitive and actually prevents
>>> mutation. This is not C++.)
>>
>>
>> We're not talking about rvalue-references... were talking about not-rvalue-references to temporaries.
>>
>
> Let me rephrase:
>
> Absolutely not. It makes absolutely no sense to force const for not-rvalue-references to temporaries. (Recall that const is transitive and actually prevents mutation. This is not C++.)

Yes, I know... why would you mutate an argument who's life doesn't extend beyond the call? Any mutation is pointless.
March 24, 2018
On 03/23/2018 07:46 PM, Jonathan M Davis wrote:
> On Friday, March 23, 2018 22:44:35 Nick Sabalausky via Digitalmars-d wrote:
>> It never made any sense to me that there could be any problem
>> with the compiler automatically creating a temporary hidden
>> lvalue so a ref could be taken. If there IS any problem, I can
>> only imagine it would be symptomatic of a different, larger
>> problem.
> 
> It can be a serious API problem if you can't look at ref and know that the
> intention is that the original argument's value will be used and then
> mutated (whereas with out, it will be mutated, but the original value won't
> be used).

???. That's equally true when an lvalue is passed in.

> However, that could be solved by having a different attribute indicate that
> the idea is that the compiler will accept rvalues and create temporaries for
> them if they're passed instead of an lvalue. Then, the situation is clear.

Why require the callee's author to add boilerplate? Just do it for all ref params that are given an rvalue.

> As for rvalue references in general, I can never remember what exactly the
> problem is. IIRC, part of it relates to the fact that it makes it impossible
> for the compiler to know whether it's dealing with an actual lvalue or not,

As long as the compiler takes the advocated "automatic hidden temporary" approach, then it *is* an actual lvalue. We're talking nothing more than sugar here. Sugar for what we're already forced to do manually. There *cannot* be a technical problem here that isn't *already* a problem with the author manually creating a temp var.