May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander Attachments:
| On 9 May 2013 17:26, Peter Alexander <peter.alexander.au@gmail.com> wrote:
> On Thursday, 9 May 2013 at 04:00:55 UTC, Manu wrote:
>
>> Perhaps you're arguing that the problem is that the user _isn't_ getting compiler complaints when the code is changed? The call that modifies it will still work fine, but it will obviously apply the changes to a temp that is then lost? Surely this is to be expected?
>>
>
> The call will not still work fine in C++.
>
> Here's the code again:
>
>
> class Collection(T) {
> ref T opIndex(size_t i) { ... }
> ...
> }
>
> void fix(ref double x) { if (isnan(x)) x = 0; }
>
> void fixAll(Collection!double c) {
> foreach (i; 0 .. c.length) {
> fix(c[i]);
> }
> }
>
>
> In (analogous) C++, if Collection's opIndex changes to return by value
> then the call to fix is a compile time error (the rvalues don't bind to
> unqualified ref). I believe Andrei is arguing that D must _at least_ do
> this to make progress, i.e. not be "worse" (than C++).
>
Ah yes, right, C++ fix() would need to be 'const ref&', but we decide in D
that 'ref const()' is too restrictive.
We're about 1 post away from finding ourselves arguing over 'scope ref'/'auto ref' again...
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Thu, 09 May 2013 04:20:02 -0400, Manu <turkeyman@gmail.com> wrote:
>
> Ah yes, right, C++ fix() would need to be 'const ref&', but we decide in D
> that 'ref const()' is too restrictive.
I have no problem with ref const accepting rvalues. As I understand it, Andrei's objection (and this may be wrong/incomplete) is that then there is no way to say you accept only lvalues as const ref and rvalues via non-ref (for performance reasons). But I think there is no technical reason preventing that with D.
-Steve
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Thursday, 9 May 2013 at 13:00:44 UTC, Steven Schveighoffer wrote:
> I have no problem with ref const accepting rvalues. As I understand it, Andrei's objection (and this may be wrong/incomplete) is that then there is no way to say you accept only lvalues as const ref and rvalues via non-ref (for performance reasons). But I think there is no technical reason preventing that with D.
Binding rvalues to ref is also dangerous, because the lifetime of the rvalue is separate from that of the ref, and the ref has no way of knowing when the rvalue will be destroyed.
The ref safety proposal will make it a runtime error to return a reference to a destroyed object, but it would be better if we reduced the opportunities for runtime errors if possible.
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander | On Thu, 09 May 2013 12:51:04 -0400, Peter Alexander <peter.alexander.au@gmail.com> wrote:
> On Thursday, 9 May 2013 at 13:00:44 UTC, Steven Schveighoffer wrote:
>> I have no problem with ref const accepting rvalues. As I understand it, Andrei's objection (and this may be wrong/incomplete) is that then there is no way to say you accept only lvalues as const ref and rvalues via non-ref (for performance reasons). But I think there is no technical reason preventing that with D.
>
> Binding rvalues to ref is also dangerous, because the lifetime of the rvalue is separate from that of the ref, and the ref has no way of knowing when the rvalue will be destroyed.
>
> The ref safety proposal will make it a runtime error to return a reference to a destroyed object, but it would be better if we reduced the opportunities for runtime errors if possible.
I agree, as long as it doesn't make it insanely difficult (i.e. current situation) to make types that deal with rvalues.
Short answer: no matter what, we have to find a way to be able to write one function that takes by ref, and accepts both rvalues and lvalues. If ref const is that way, so be it. If auto ref is that way, so be it. If it's plain-old ref, I'm fine with that too.
-Steve
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | > I agree, as long as it doesn't make it insanely difficult (i.e. current situation) to make types that deal with rvalues.
>
> Short answer: no matter what, we have to find a way to be able to write one function that takes by ref, and accepts both rvalues and lvalues. If ref const is that way, so be it. If auto ref is that way, so be it. If it's plain-old ref, I'm fine with that too.
>
> -Steve
We have been looking for a solution since years. ;)
It will take a very long long time. That is certain.
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Thursday, 9 May 2013 at 17:27:41 UTC, Steven Schveighoffer wrote:
> Short answer: no matter what, we have to find a way to be able to write one function that takes by ref, and accepts both rvalues and lvalues. If ref const is that way, so be it. If auto ref is that way, so be it. If it's plain-old ref, I'm fine with that too.
Allowing plain-old ref to bind to rvalues would be a massive mistake in my opinion. See case (2) in the original post.
It seems that 'auto ref' would be suitable, provided we can find a way for it to work with normal functions (in a sensible way, not like templates).
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander | On Thu, 09 May 2013 13:45:16 -0400, Peter Alexander <peter.alexander.au@gmail.com> wrote: > On Thursday, 9 May 2013 at 17:27:41 UTC, Steven Schveighoffer wrote: >> Short answer: no matter what, we have to find a way to be able to write one function that takes by ref, and accepts both rvalues and lvalues. If ref const is that way, so be it. If auto ref is that way, so be it. If it's plain-old ref, I'm fine with that too. > > Allowing plain-old ref to bind to rvalues would be a massive mistake in my opinion. See case (2) in the original post. The case for it is operator overloads. Note that all operator overloads are defined WITHIN types, and all operator overloads take 'this' by reference. Without the hack currently in the compiler that 'this' is allowed to bind by ref to rvalues, D would be near useless. Consider that today, this doesn't work for custom structs, unless you allocate the result on the heap. ((a + b) + (c + d)) The original case 2 looks bad, but not very common. With by-ref turning into by-value, you are in effect changing the API and functionality. It would just mean that you wouldn't make that change, or change the type name at the same time (so it wouldn't just compile on existing code). I think someone later mentioned that if 'fix' was a method, it would break even today (with ref-binds-to-this hack). You should also consider that even if we break existing code by changing by-ref into by-value, there's no guarantee the person maintaining the code does the right thing. > It seems that 'auto ref' would be suitable, provided we can find a way for it to work with normal functions (in a sensible way, not like templates). I think first we have to prove that auto ref is worth the extra cognitive load. Every overload style we add has a large multiplicative effect on boilerplate. As I said before, I'd be ok with any solution, as long as we can bind rvalues to some form of ref. -Steve |
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Thursday, May 09, 2013 14:23:11 Steven Schveighoffer wrote:
> I think first we have to prove that auto ref is worth the extra cognitive load. Every overload style we add has a large multiplicative effect on boilerplate.
>
> As I said before, I'd be ok with any solution, as long as we can bind rvalues to some form of ref.
We already have it with templated functions. For the most part, there would be no real difference from the caller's perspective between auto ref with a templated function and auto ref with a non-templated functions. You just use auto ref (or auto ref const if you can use const) whenever you want a parameter to accept both rvalues and lvalues, and whether the function is templated or not doesn't really matter.
The only times that it would matter are when you actually care about forwarding the refness of the argument (which the templated functon will do but not the non-templated one) or when you don't want the extra template bloat of the templated auto ref (though it might be possible for the compiler to use the non-templated solution for auto ref when it can determine that ref forwarding isn't being used). Using auto ref in this manner is exactly what it was originally intended for and exactly what TDPL describes. So, I don't think that the difference between templated auto ref and non-templated auto ref will add much cognitive load, and we've already added the cognitive load of having auto ref by having it with templated functions.
- Jonathan M Davis
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Thursday, May 09, 2013 09:00:43 Steven Schveighoffer wrote:
> On Thu, 09 May 2013 04:20:02 -0400, Manu <turkeyman@gmail.com> wrote:
> > Ah yes, right, C++ fix() would need to be 'const ref&', but we decide in
> > D
> > that 'ref const()' is too restrictive.
>
> I have no problem with ref const accepting rvalues. As I understand it, Andrei's objection (and this may be wrong/incomplete) is that then there is no way to say you accept only lvalues as const ref and rvalues via non-ref (for performance reasons). But I think there is no technical reason preventing that with D.
Given how restrictive const is in D, I think that it would be a mistake to make it so that the way to make a ref accept rvalues is by using const.
- Jonathan M Davis
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander | On Thursday, May 09, 2013 19:45:16 Peter Alexander wrote:
> It seems that 'auto ref' would be suitable, provided we can find a way for it to work with normal functions (in a sensible way, not like templates).
That's trivial enough. All you have to do is lower code like
auto foo(auto ref int i) {...}
foo(5);
to something like
auto foo(ref int i) {...}
auto __temp = 5;
foo(__temp);
And temporaries end up on the stack anyway, so you wouldn't really even have to lower it to quite like that, but that's what would be happening conceptually. It's also what would happen if plain ref accepted rvalues. It's just that we avoid certain classes of issues by having something distinct from plain ref for accepting rvalues by ref. The implementation itself is straightforward. If anything, I think that argument comes down primarily to two things:
1. Should plain ref accept rvlaues?
2. If plain ref shouldn't accept rvalues, then what attribute do we use to accept rvalues by ref?
And given that the whole point of adding auto ref to the language was to solve exactly this problem, I think that it makes perfect sense to just use auto ref.
- Jonathan M Davis
|
Copyright © 1999-2021 by the D Language Foundation