May 05, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | I wish to add to the discussion that you want to pass by ref for 2 reasons (and the intent is very different) : - You intend to modify the value in some meaningful way for the caller. In which case, binding to rvalue don't make any sense. - You want to avoid creating copies, when this isn't necessary. In this case, this is a performance reason, and binding to rvalue make sense. Note that knowing when to pass by value or by reference for performance is more tricky than many people tend to think. The reason is simple : you win an indirection, so if the data are small enough, and especially is the ABI say they are passed via registers, this is a win to pass by value. Many C++ dev will tell you that this is faster, but the fact is that C++ compiler can transform pass by ref into pass by value when it speedup things. It is especially hard problem to solve in generic code. Here is to state the problem. Now let's discuss solutions. As shown before the problem is about performance? And when it come to performance, the first thing we should look for is to allow the optimizer to kick in. The first change I'd propose is to allow the compiler to optimize away all pure copy operation (which include memory allocation). I don't know how far we can go with that, but that is IMO the first direction we should explore. It is simpler for the dev, less error prone, will improve all programs, and do not require any new feature. I'm well aware that the famous sufficiently smart compiler may lurk here, and so we may need to come back on the issue later if that approach fail. It is still worth trying. |
May 05, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | Few additional points: On Sunday, 5 May 2013 at 16:07:12 UTC, deadalnix wrote: > I wish to add to the discussion that you want to pass by ref for 2 reasons (and the intent is very different) : > - You intend to modify the value in some meaningful way for the caller. In which case, binding to rvalue don't make any sense. You may want to modify rvalue temporary for implementing some sort of move semantics like in C++11. > - You want to avoid creating copies, when this isn't necessary. In this case, this is a performance reason, and binding to rvalue make sense. Not necessarily performance reason on its own, copy construction of given object may have an undesired side effects. Or it may be even some singleton'ish object. |
May 05, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Sunday, 5 May 2013 at 16:14:01 UTC, Dicebot wrote: > Few additional points: > > On Sunday, 5 May 2013 at 16:07:12 UTC, deadalnix wrote: >> I wish to add to the discussion that you want to pass by ref for 2 reasons (and the intent is very different) : >> - You intend to modify the value in some meaningful way for the caller. In which case, binding to rvalue don't make any sense. > > You may want to modify rvalue temporary for implementing some sort of move semantics like in C++11. > No, you won't be able to reach any of this anyway. Move semantic is already cleanly implemented in D, so the C++ mess is irrelevant here. >> - You want to avoid creating copies, when this isn't necessary. In this case, this is a performance reason, and binding to rvalue make sense. > > Not necessarily performance reason on its own, copy construction of given object may have an undesired side effects. Or it may be even some singleton'ish object. Having a singleton as a value type, and not marking the postblit as @disable is asking for trouble in general, nothing specific to the discussion here. |
May 05, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On 5/5/13 3:00 AM, Jonathan M Davis wrote:
> On Sunday, May 05, 2013 01:49:42 Andrei Alexandrescu wrote:
>> There may be other important patterns to address at the core, please
>> chime in. I consider (1) above easy to tackle, which leaves us with at
>> least (2). My opinion is that any proposal for binding rvalues to ref
>> must offer a compelling story about these patterns.
>
> Another case is when you want to distinguish between lvalues and rvalues.
Good one. This is in fact what happened in C++: const T& binds so tightly to T rvalues, that it was impossible to work a wedge in between to allow overloading on T and on const T&. Therefore, a much heavier solution was needed.
Andrei
|
May 05, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On 5/5/13 7:55 AM, Timon Gehr wrote:
> I still think auto ref should be extended to work for non-templated
> functions. The semantics should be the same (i.e. behave as if there
> were two copies that are eagerly semantically analyzed, but only
> generate code for one) where possible. It would be an error to have
> different behaviour of the two "copies". This would mean that in
> non-templated functions, it should be an error to rely on whether or not
> the passed argument was an lvalue. i.e. (auto) ref return of an auto ref
> argument (at least in @safe code) and __traits(isRef,autoRefArgument)
> should be disallowed. This would have the effect of making it illegal to
> return references to temporaries from functions (at least in @safe
> code), and hence the life time of rvalues would not have to be changed.
>
> Both (1) and (2) would be close to non-issues with this solution.
I think the same. For non-templates "auto ref" should simply accept rvalues and lvalues alike. Inside the function an "auto ref" parameter is handled as a local (i.e. disappears at the end of the function) so the compiler disallows returning it by reference directly with the current algorithms. For more complicated patterns, dynamic checking will pick the slack.
I'll think I'll rework my DIP for that.
Andrei
|
May 05, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Sunday, 5 May 2013 at 16:21:37 UTC, deadalnix wrote: > No, you won't be able to reach any of this anyway. Move semantic is already cleanly implemented in D, so the C++ mess is irrelevant here. Agreed. >>> - You want to avoid creating copies, when this isn't necessary. In this case, this is a performance reason, and binding to rvalue make sense. >> >> Not necessarily performance reason on its own, copy construction of given object may have an undesired side effects. Or it may be even some singleton'ish object. > > Having a singleton as a value type, and not marking the postblit as @disable is asking for trouble in general, nothing specific to the discussion here. Yes, sure, but you will still use ref if you want ( don't ask why :) ) to use it as an argument to a function and not because of performance but because it is only option. I was objecting to stating that "avoiding copies" -> "performance reasons". |
May 06, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sat, 04 May 2013 22:49:42 -0700, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > void fix(ref double x) { if (isnan(x)) x = 0; } ... > There may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns. But there are reasons to bind rvalues to references. I think deadalnix said it best, when you want to use ref to modify values, generally you want rvalues to be rejected (though I think rvalue is not a good description, consider that a pointer is an rvalue, but what it points to is an lvalue). When you want to use ref to avoid expensive copies, you want rvalues to be accepted. So I agree with your solution to 1. For 2, I think it's not so simple. A possible solution is to make fix accept a pointer, which is an explicit reference and would not bind to rvalues. In @safe code, a pointer could be allowed, just pointer arithmetic disallowed. In addition, Peter's case makes this even more disruptive. 'this' must accept rvalues, so it's not possible to eliminate the problem by preventing binding. -Steve |
May 06, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 5/6/13 12:56 PM, Steven Schveighoffer wrote:
> On Sat, 04 May 2013 22:49:42 -0700, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> void fix(ref double x) { if (isnan(x)) x = 0; }
>
> ...
>
>> There may be other important patterns to address at the core, please
>> chime in. I consider (1) above easy to tackle, which leaves us with at
>> least (2). My opinion is that any proposal for binding rvalues to ref
>> must offer a compelling story about these patterns.
>
> But there are reasons to bind rvalues to references. I think deadalnix
> said it best, when you want to use ref to modify values, generally you
> want rvalues to be rejected (though I think rvalue is not a good
> description, consider that a pointer is an rvalue, but what it points to
> is an lvalue). When you want to use ref to avoid expensive copies, you
> want rvalues to be accepted.
Yah, so that's why I'm thinking "auto ref" would fit the bill there. Requiring pointers for lvalue binding and binding loosely to ref seems like the wrong move.
Andrei
|
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu Attachments:
| On 7 May 2013 00:26, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>wrote: > On 5/6/13 12:56 PM, Steven Schveighoffer wrote: > >> On Sat, 04 May 2013 22:49:42 -0700, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org**> wrote: >> >> void fix(ref double x) { if (isnan(x)) x = 0; } >>> >> >> ... >> >> There may be other important patterns to address at the core, please >>> chime in. I consider (1) above easy to tackle, which leaves us with at >>> least (2). My opinion is that any proposal for binding rvalues to ref >>> must offer a compelling story about these patterns. >>> >> >> But there are reasons to bind rvalues to references. I think deadalnix said it best, when you want to use ref to modify values, generally you want rvalues to be rejected (though I think rvalue is not a good description, consider that a pointer is an rvalue, but what it points to is an lvalue). When you want to use ref to avoid expensive copies, you want rvalues to be accepted. >> > > Yah, so that's why I'm thinking "auto ref" would fit the bill there. Requiring pointers for lvalue binding and binding loosely to ref seems like the wrong move. ... I feel like this discussion has jumped about 1 week back in time :/ In 1) "If rvalues bind indiscriminately to ref, then the call is legal because of the implicit conversion float->double" Implicit conversion should never be allowed when receiving by ref, just like it's an error in C++. There should never be a case where, having passed an lvalue, the function receives a magic temp from an implicit conversion. C++ correctly doesn't allow this, but lots of people here seem to think it does... In 2) "Changing return types from ref T to T or back and expecting no ill effects (aside from fixing compile-time errors) is a frequent operation in C++ projects I'm involved in. Doing worse than that would be arguably a language design regression." Can you show where you would ever make such a change in C++? What do you mean by 'worse'? The behaviour is to be expected... if opIndex doesn't return a ref, why would you expect that the caller can possibly change it? I don't see any problem here... what 'ill effects' are you referring to? 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? |
May 09, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | 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++).
|
Copyright © 1999-2021 by the D Language Foundation