www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The liabilities of binding rvalues to ref

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Here are the issues that need to be addressed by any solution 
reconciling rvalues and passing into functions. This post is not arguing 
for any particular solution or approach, but lays down the issues that 
any approach must be judged by.

1. The LRL (Lvalue-Rvalue-Lvalue) problem.

This has been long mentioned as an argument in defining C++'s 
references. The crux of the issue is that caller code passes an lvalue, 
and the callee code receives an lvalue, but in the middle there's an 
rvalue created by an implicit conversion. Consider:

void fix(ref double x) { if (isnan(x)) x = 0; }
...
float a;
...
fix(a);

If rvalues bind indiscriminately to ref, then the call is legal because 
of the implicit conversion float->double.

A possible solution is to disallow binding if the initial value bound is 
an lvalue.

2. Code evolution.

Jonathan mentioned this too. The problem here is that as code evolves, 
meaningful code doing real work becomes silently useless code that 
patently does nothing. Consider:

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]);
   }
}

As design evolves, Collection's opIndex may change to return a T instead 
of ref T (e.g. certain implementations of sparse vectors). When that 
happens, the caller code will continue to compile and run. However, it 
won't do anything interesting: fix will be always called against a 
temporary plucked from the collection.

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.

Note that in function call chains fun(gun(hun())), which are common 
(written as fun.gun.hun etc) in the increasingly popular pipeline-style 
of defining processing, one function changing return style poisons the 
well for everybody down the pipeline. That may lead to pipelines that 
have only partial effect.

=======

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.


Andrei
May 04 2013
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
One solution poffered was making rvalues only bindable to const ref. Andrei's 
position was that this prevented many important use cases.

The trouble was that const is transitive, and yet what was desired here was 
so-called "head const". D doesn't have a concept of head const.

(C++ const is always "head const".)
May 04 2013
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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. In fact, IIRC this came up in Ali's dconf talk. He had an opAssign which swapped guts with its argument when it was an rvalue (since it was known to be a temporary) and did a more normal assignment when it was an lvalue. You might still be able to do that if ref accepts rvalues (because the non-ref overload would be preferred in the rvalue case, and the ref overload would be preferred in the lvalue case), but I suspect that that would be incredibly error-prone - especially when there are multiple arguments to the function. So, whatever solution we go with needs to allow us to reasonably overload on refness when we want to while still being able to have functions which accept both lvalues and rvalues by reference. - Jonathan M Davis
May 05 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
Is there any intention to address the issue of the lvalue-ness of 
"this"? In C++, *this is always an lvalue, even if the member 
function was called with an rvalue, leading to situations like 
this:

struct Number
{
     void fix() { if (isnan(x)) x = 0; }
     double x;
}

class Collection(T) {
   ref T opIndex(size_t i) { ... }
   ...
}

void fixAll(Collection!Number c) {
   foreach (i; 0 .. c.length) {
     c[i].fix();
   }
}


Here, if Collection changes to return by non-ref then the fix() 
call is still valid, silently doing nothing of value. Analogous 
code in C++ is allowed as well.

Do we intend to fix this as well? I suspect there are use cases 
where such calls are useful, but I can't think of any right now.
May 05 2013
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/05/2013 07:49 AM, 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.
 ...
Any solution that allows both lvalues and rvalues to bind to the same reference will have problem (2), and fixing up problem (1) introduces somewhat arbitrary rules. Probably we do not want to have to deal with them by default. A possibility would be to fix (2) in a similar way to the proposed solution for (1): Only allow rvalues to bind to 'ref' if they cannot be turned into lvalues by changing the signature of a different function. I don't like this solution very much. 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.
May 05 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/05/2013 01:55 PM, Timon Gehr wrote:
 On 05/05/2013 07:49 AM, 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.
 ...
Any solution that allows both lvalues and rvalues to bind to the same reference will have problem (2), and fixing up problem (1) introduces somewhat arbitrary rules. Probably we do not want to have to deal with them by default. A possibility would be to fix (2) in a similar way to the proposed solution for (1): Only allow rvalues to bind to 'ref' if they cannot be turned into lvalues by changing the signature of a different function. I don't like this solution very much. 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.
It seems to make sense that template function instantiations should be split into two copies per auto ref argument only if it is necessary to support differing semantics for lvalues and rvalues. Peter Alexander brings up a good point regarding the implicit this reference for structs. Probably it should be passed by auto ref.
May 05 2013
prev sibling next sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 5 May 2013 at 11:55:28 UTC, 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.
This is not the case currently for template auto ref functions. For example, the two "versions" of the auto ref function do not share local static variables, nor do they have the same function address. They are literally two (or four, or eight, ...) separate functions. I really dislike how they have been implemented.
May 05 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/05/2013 02:11 PM, Peter Alexander wrote:
 On Sunday, 5 May 2013 at 11:55:28 UTC, 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.
This is not the case currently for template auto ref functions. For example, the two "versions" of the auto ref function do not share local static variables, nor do they have the same function address.
Very good point about the local static variables. This would be a wart of my proposed solution, as they'd need to be banned for auto ref functions.
 They are literally two (or four, or eight, ...) separate functions. I really
 dislike how they have been implemented.
Me too, but it makes sense to have two functions in some cases.
May 05 2013
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/05/2013 01:55 PM, Timon Gehr wrote:
 ...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. ...
It also would need to be an error to have local static variables.
May 05 2013
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 5 May 2013 at 05:49:42 UTC, 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.
Yes ! With optional parenthesis, it is super unclear if we access an lvalue or an rvalue and update its content or not. A good example of that is the captures property from std.regex. The property is an revalue, and would look like a filed that reset itself all the time if bound to an lvalue.
May 05 2013
prev sibling next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
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
parent reply "Dicebot" <m.strashun gmail.com> writes:
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
parent reply "deadalnix" <deadalnix gmail.com> writes:
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
parent "Dicebot" <m.strashun gmail.com> writes:
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 05 2013
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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 06 2013
parent reply Manu <turkeyman gmail.com> writes:
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 08 2013
parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
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++).
May 09 2013
parent reply Manu <turkeyman gmail.com> writes:
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
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
next sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
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
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 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
prev sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
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
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
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
prev sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Thursday, 9 May 2013 at 18:23:12 UTC, Steven Schveighoffer 
wrote:
 On Thu, 09 May 2013 13:45:16 -0400, Peter Alexander
 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.
C++ has this "hack" as well. Is it really a hack, or just part of the language design?
 Consider that today, this doesn't work for custom structs, 
 unless you allocate the result on the heap.

 ((a + b) + (c + d))
I don't follow. You say "today", but this works for me in latest dmd. Do you mean hypothetically without the "hack", or am I misunderstanding?
 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'm not sure about how common it is, but I really don't like the idea of calls like swap(1, 2) being legal. Seems like a step backward from C++.
May 09 2013
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 09 May 2013 16:36:27 -0400, Peter Alexander  
<peter.alexander.au gmail.com> wrote:

 On Thursday, 9 May 2013 at 18:23:12 UTC, Steven Schveighoffer wrote:
 On Thu, 09 May 2013 13:45:16 -0400, Peter Alexander
 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.
C++ has this "hack" as well. Is it really a hack, or just part of the language design?
I should restate: The idea that restricting rvalues from binding to refs *except* the case where it binds to 'this' is a hack. The binding to 'this' pre-dates the restriction.
 Consider that today, this doesn't work for custom structs, unless you  
 allocate the result on the heap.

 ((a + b) + (c + d))
I don't follow. You say "today", but this works for me in latest dmd. Do you mean hypothetically without the "hack", or am I misunderstanding?
I mean it doesn't work today if it takes the parameter by ref.
 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'm not sure about how common it is, but I really don't like the idea of calls like swap(1, 2) being legal. Seems like a step backward from C++.
Why is it so bad that that is legal? Really hard to stop people from writing incorrect code. In your case, it actually LOOKS wrong, so that's not really a case we should be concerned about. The original case 2 is worse. int i = 1; swap(i, i); That's also legal. *(cast(int *)null) = 5; That's also legal. -Steve
May 09 2013
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, May 09, 2013 16:59:13 Steven Schveighoffer wrote:
 Why is it so bad that that is legal? Really hard to stop people from
 writing incorrect code. In your case, it actually LOOKS wrong, so that's
 not really a case we should be concerned about.
The problem is that while it's obvious in the swap case, it's not always obvious. And when you look at a function signature and see that it accepts its argument by ref (and therefore doesn't accept rvalues), you can reasonably assume that it's intended to alter its argument, whereas if it takes by auto ref (which then would accept rvalues), you know that the intention is for the function to accept arguments by ref for efficiency and not for mutation. You do need const to fully guarantee that, but it still gives a definite indicator of intent even without that (and const is too restrictive for some cases to require it). And if ref accepted rvalues, you'd end up with functions using ref as a matter of course for the efficiency gain (as occurs in C++ with const&) and wouldn't have a clue which ones were intended to mutate their arguments and which were just doing it for efficiency (since unlike C++, const wouldn't be required and might even be avoided given how restrictive it is in D). - Jonathan M Davis
May 09 2013
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 09 May 2013 17:06:43 -0400, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Thursday, May 09, 2013 16:59:13 Steven Schveighoffer wrote:
 Why is it so bad that that is legal? Really hard to stop people from
 writing incorrect code. In your case, it actually LOOKS wrong, so that's
 not really a case we should be concerned about.
The problem is that while it's obvious in the swap case, it's not always obvious. And when you look at a function signature and see that it accepts its argument by ref (and therefore doesn't accept rvalues), you can reasonably assume that it's intended to alter its argument, whereas if it takes by auto ref (which then would accept rvalues), you know that the intention is for the function to accept arguments by ref for efficiency and not for mutation. You do need const to fully guarantee that, but it still gives a definite indicator of intent even without that (and const is too restrictive for some cases to require it). And if ref accepted rvalues, you'd end up with functions using ref as a matter of course for the efficiency gain (as occurs in C++ with const&) and wouldn't have a clue which ones were intended to mutate their arguments and which were just doing it for efficiency (since unlike C++, const wouldn't be required and might even be avoided given how restrictive it is in D).
I understand the point, but it's not as bad as you think. Usually you can discern from the function name what its going to do (e.g. the 'fix' function from the example). Having a storage class that accepts by ref and then *might* change them is really kind of useless. If auto ref is used, there should be no promises for that storage class. In any case, I'm fine with auto ref if that is the solution. I just wonder if it's worth the trouble. I suppose we will know the answer only after having it implemented for a while. -Steve
May 09 2013
prev sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Thursday, 9 May 2013 at 20:59:14 UTC, Steven Schveighoffer 
wrote:
 I should restate: The idea that restricting rvalues from 
 binding to refs *except* the case where it binds to 'this' is a 
 hack.  The binding to 'this' pre-dates the restriction.
Ah ok. I do agree that the asymmetry is quite hacky, although binding to 'this' isn't quite the same as binding to ref.
 I'm not sure about how common it is, but I really don't like 
 the idea of calls like swap(1, 2) being legal. Seems like a 
 step backward from C++.
Why is it so bad that that is legal? Really hard to stop people from writing incorrect code.
You could apply the same argument against static typing, e.g. "swap(apple, orange) looks wrong, so why is it so bad not to catch the type error at compile time?" I think we can agree that while it's not possible to stop people from writing incorrect code 100% of the time, there is still benefit to catching as many cases as practical.
May 09 2013
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 09 May 2013 17:45:33 -0400, Peter Alexander  
<peter.alexander.au gmail.com> wrote:

 On Thursday, 9 May 2013 at 20:59:14 UTC, Steven Schveighoffer wrote:
 I should restate: The idea that restricting rvalues from binding to  
 refs *except* the case where it binds to 'this' is a hack.  The binding  
 to 'this' pre-dates the restriction.
Ah ok. I do agree that the asymmetry is quite hacky, although binding to 'this' isn't quite the same as binding to ref.
It's exactly the same actually. 'this' is an implicit ref argument. I don't see how it's any different. We also currently have a hack where ++ doesn't work on rvalues artificially, but an .increment() function does.
 I'm not sure about how common it is, but I really don't like the idea  
 of calls like swap(1, 2) being legal. Seems like a step backward from  
 C++.
Why is it so bad that that is legal? Really hard to stop people from writing incorrect code.
You could apply the same argument against static typing, e.g. "swap(apple, orange) looks wrong, so why is it so bad not to catch the type error at compile time?" I think we can agree that while it's not possible to stop people from writing incorrect code 100% of the time, there is still benefit to catching as many cases as practical.
I define practical as catching as many things that shouldn't work as you can without making it impossible to write things that should. -Steve
May 09 2013
prev sibling next sibling parent Dmitry S <ds.dlang gmail.com> writes:
 I'm not sure about how common it is, but I really don't like the idea of
 calls like swap(1, 2) being legal. Seems like a step backward from C++.
There are useful analogs, however. E.g. consider heap.swap(parentIndex, parentIndex * 2 + 1) heap.swap(getParent(index), index) It's convenient and intuitive if the lvalues in these calls can be accepted by reference and modified. And it's not a problem that the modification to the rvalue is lost -- that's what's desired in this case. One alternative which I haven't seen (though I haven't read the entire thread) is to require the caller to add some syntax when passing an rvalue to a ref parameter. E.g. in the above example heap.swap(parentIndex, tempRef(parentIndex * 2 + 1)) would look a little clearer, by making it more explicit what's going on with that second parameter. I imagine "tempRef" would have to be some language feature, a sort of alternative to "auto ref", but to be used at the call site. Dmitry
May 09 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/13 4:36 PM, Peter Alexander wrote:
 I'm not sure about how common it is, but I really don't like the idea of
 calls like swap(1, 2) being legal. Seems like a step backward from C++.
I think if we ever get swap(1, 2) to compile and run we'd effectively have destroyed the D programming language. Andrei
May 09 2013
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 5/9/13 4:36 PM, Peter Alexander wrote:
 I'm not sure about how common it is, but I really don't like the idea of
 calls like swap(1, 2) being legal. Seems like a step backward from C++.
I think if we ever get swap(1, 2) to compile and run we'd effectively have destroyed the D programming language.
Depends on context. int swap(int diskNum, int partitionNum); Someone pointed out that swap could be a function that swaps heap indexes, and might even take by ref not caring if you want the resulting value that was swapped. with(someHeap) { swap(1, 2); // swap indexes 1 and 2 } -Steve
May 09 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/13 10:05 PM, Steven Schveighoffer wrote:
 On Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 5/9/13 4:36 PM, Peter Alexander wrote:
 I'm not sure about how common it is, but I really don't like the idea of
 calls like swap(1, 2) being legal. Seems like a step backward from C++.
I think if we ever get swap(1, 2) to compile and run we'd effectively have destroyed the D programming language.
Depends on context. int swap(int diskNum, int partitionNum); Someone pointed out that swap could be a function that swaps heap indexes, and might even take by ref not caring if you want the resulting value that was swapped. with(someHeap) { swap(1, 2); // swap indexes 1 and 2 } -Steve
Now that's great trolling! Andrei
May 09 2013
next sibling parent reply Dmitry S <ds.dlang gmail.com> writes:
On Thu, May 9, 2013 at 10:14 PM, Andrei Alexandrescu <
SeeWebsiteForEmail erdani.org> wrote:

 On 5/9/13 10:05 PM, Steven Schveighoffer wrote:

 On Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org**> wrote:

  On 5/9/13 4:36 PM, Peter Alexander wrote:
 I'm not sure about how common it is, but I really don't like the idea of
 calls like swap(1, 2) being legal. Seems like a step backward from C++.
I think if we ever get swap(1, 2) to compile and run we'd effectively have destroyed the D programming language.
Depends on context. int swap(int diskNum, int partitionNum); Someone pointed out that swap could be a function that swaps heap indexes, and might even take by ref not caring if you want the resulting value that was swapped. with(someHeap) { swap(1, 2); // swap indexes 1 and 2 } -Steve
Now that's great trolling!
To clear my name, just in case: I wasn't trolling. I have a use case (in a heap implementation): void indexSwap(ref int a, ref int b) { swap(array[a], array[b]); swap(a, b); } It would be great to be able to call indexSwap(index, index*2 + 1) in one line without having to create a named temporary for the second argument, and with having it mutate index to be index*2 + 1. I think auto ref solves it, though a call-site solution (an inline way to create that temporary) seems like it would work too. Dmitry
May 09 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/13 10:23 PM, Dmitry S wrote:
 To clear my name, just in case: I wasn't trolling.
And to clear mine: I was referring to std.swap, which swaps two values. Andrei
May 09 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 09 May 2013 22:14:33 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 5/9/13 10:05 PM, Steven Schveighoffer wrote:
 On Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 On 5/9/13 4:36 PM, Peter Alexander wrote:
 I'm not sure about how common it is, but I really don't like the idea  
 of
 calls like swap(1, 2) being legal. Seems like a step backward from  
 C++.
I think if we ever get swap(1, 2) to compile and run we'd effectively have destroyed the D programming language.
Depends on context. int swap(int diskNum, int partitionNum); Someone pointed out that swap could be a function that swaps heap indexes, and might even take by ref not caring if you want the resulting value that was swapped. with(someHeap) { swap(1, 2); // swap indexes 1 and 2 } -Steve
Now that's great trolling!
That's not great trolling, it's *clever* trolling ;) I was actually half serious though, the name of the function is really important here, including the context. -Steve
May 09 2013
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
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
May 09 2013
parent reply "Rob T" <alanb ucora.com> writes:
On Thursday, 9 May 2013 at 19:26:37 UTC, Jonathan M Davis wrote:
 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
So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.
May 09 2013
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/13 3:39 PM, Rob T wrote:
 So, if I understand correctly, auto ref for templates will end up doing
 exactly the same thing as auto ref for non-template functions? That
 would be perfect, otherwise it'll be terribly confusing.
There would be clear restrictions on non-template functions, e.g. a non-template cannot return auto ref. Andrei
May 09 2013
next sibling parent reply "Rob T" <alanb ucora.com> writes:
On Thursday, 9 May 2013 at 19:43:25 UTC, Andrei Alexandrescu 
wrote:
 On 5/9/13 3:39 PM, Rob T wrote:
 So, if I understand correctly, auto ref for templates will end 
 up doing
 exactly the same thing as auto ref for non-template functions? 
 That
 would be perfect, otherwise it'll be terribly confusing.
There would be clear restrictions on non-template functions, e.g. a non-template cannot return auto ref. Andrei
OK I can understand that auto ref on return for a template function is something different, so disallowing auto ref on a regular function makes perfect sense to me. You can also create template functions with auto ref params that behave exactly like a non-template counter part by not specifying ref on the return, so it seems consistent and a sound solution from that perspective. D is a complex language, so stuff like this does take some getting used to, but it is very powerful and flexible, no two ways around it.
May 09 2013
parent reply Manu <turkeyman gmail.com> writes:
On 10 May 2013 06:07, Rob T <alanb ucora.com> wrote:

 On Thursday, 9 May 2013 at 19:43:25 UTC, Andrei Alexandrescu wrote:

 On 5/9/13 3:39 PM, Rob T wrote:

 So, if I understand correctly, auto ref for templates will end up doing
 exactly the same thing as auto ref for non-template functions? That
 would be perfect, otherwise it'll be terribly confusing.
There would be clear restrictions on non-template functions, e.g. a non-template cannot return auto ref. Andrei
OK I can understand that auto ref on return for a template function is something different, so disallowing auto ref on a regular function makes perfect sense to me.
It doesn't make sense though. You're creating an arbitrary restriction because overloading an existing meaning doesn't apply (actually conflicts) in this case. auto is a template concept, it should not be applied here. Refer to all the reasoning behind scope ref instead... You can also create template functions with auto ref params that behave
 exactly like a non-template counter part by not specifying ref on the
 return, so it seems consistent and a sound solution from that perspective.
Umm, what if the non-template counterpart returns ref? Then it doesn't behave the same. D is a complex language, so stuff like this does take some getting used to,
 but it is very powerful and flexible, no two ways around it.
If this takes 'getting used to', you're basically admitting that it doesn't make intuitive sense.
May 09 2013
next sibling parent reply "Rob T" <alanb ucora.com> writes:
On Thursday, 9 May 2013 at 21:55:51 UTC, Manu wrote:
 Umm, what if the non-template counterpart returns ref? Then it 
 doesn't
 behave the same.
Yes, the non-template version cannot ref return an auto ref param, but I would expect that neither can the template version unless auto ref is specified as the return value. The key difference that I think can be understood is that auto ref on the return value for a template is not the same thing as auto ref on the param. For templates, you can enforce exactly the same behavior as the non-template version by specifying a return of either ref or not, ie you do not specify auto ref or you specify a return by value. If you so happen to specify a ref return of an auto ref param, I would expect the compiler to refuse to compile no matter if it is a template function or not. Of course you can confuse the programmer and allow such a thing to compile only for the cases where it can return ref on the auto ref, but IMO that'll be a big mistake as it will confuse the s*** out of most people, and besides allowing something like that has no value at all.
 D is a complex language, so stuff like this does take some 
 getting used to,
 but it is very powerful and flexible, no two ways around it.
If this takes 'getting used to', you're basically admitting that it doesn't make intuitive sense.
Well, I suppose I cannot disagree with you on that point, so yes it is confusing, but for sake of a solution, it is not nearly as confusing so long as auto ref on the parameters will behave the same for both template and non-template versions in a consistent way. I know auto ref on the return is potentially confusing, but it does increase template flexibility as the benefit. Alternatively, I think Jonathan's argument against scope ref makes perfect sense. Unless I misread something or he's dead wrong, how can scope be used without creating even more confusion? Even if scope has some properties in common with auto ref, it specifies something entirely different than accepting rvalues and lvalues. This point reminds me of why we should not be using bool as an integral type, it's the same reasoning. --rt
May 09 2013
next sibling parent reply Manu <turkeyman gmail.com> writes:
On 10 May 2013 09:01, Rob T <alanb ucora.com> wrote:

 On Thursday, 9 May 2013 at 21:55:51 UTC, Manu wrote:

 Umm, what if the non-template counterpart returns ref? Then it doesn't
 behave the same.
Yes, the non-template version cannot ref return an auto ref param, but I would expect that neither can the template version unless auto ref is specified as the return value. The key difference that I think can be understood is that auto ref on the return value for a template is not the same thing as auto ref on the param. For templates, you can enforce exactly the same behavior as the non-template version by specifying a return of either ref or not, ie you do not specify auto ref or you specify a return by value. If you so happen to specify a ref return of an auto ref param, I would expect the compiler to refuse to compile no matter if it is a template function or not. Of course you can confuse the programmer and allow such a thing to compile only for the cases where it can return ref on the auto ref, but IMO that'll be a big mistake as it will confuse the s*** out of most people, and besides allowing something like that has no value at all. D is a complex language, so stuff like this does take some getting used
 to,

 but it is very powerful and flexible, no two ways around it.
If this takes 'getting used to', you're basically admitting that it doesn't make intuitive sense.
Well, I suppose I cannot disagree with you on that point, so yes it is confusing, but for sake of a solution, it is not nearly as confusing so long as auto ref on the parameters will behave the same for both template and non-template versions in a consistent way. I know auto ref on the return is potentially confusing, but it does increase template flexibility as the benefit.
It IS confusing that auto-ref would do 2 completely different things. One automatically selecting ref-ness, the other saying "i can safely receive a temporary". There is nothing 'automatic' about the latter. Alternatively, I think Jonathan's argument against scope ref makes perfect
 sense. Unless I misread something or he's dead wrong, how can scope be used
 without creating even more confusion? Even if scope has some properties in
 common with auto ref, it specifies something entirely different than
 accepting rvalues and lvalues. This point reminds me of why we should not
 be using bool as an integral type, it's the same reasoning.
Which argument? Correct, it specifies something _entirely different_, it says "I can safely receive a temporary, because I promise not to escape it". This is the actual problem that we're trying to solve, and it addresses the problem head on. As I've had to re-iterate countless times, and such is the massive fallacy behind all of these threads, this whole debate is NOT about lvalues/rvalues, and I wish people would stop using the term 'rvalue' in their posts, I worry that they misunderstand the problem every time it's said. This code is broken: void f(ref int x) {} int x; f(x); x is an lvalue. This is the real problem case, and addressing this will solve the rvalue case at the same time. Passing an rvalue to a function just generates an implicit temp which is functionally identical to the above, except the lifetime of a temp is usually the life of the statement rather than the outer scope. The problem we need to solve is that of a function being able to safely receive a _temporary_.
May 09 2013
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/13 8:08 PM, Manu wrote:
 On 10 May 2013 09:01, Rob T <alanb ucora.com <mailto:alanb ucora.com>>
 wrote:
 It IS confusing that auto-ref would do 2 completely different things.
 One automatically selecting ref-ness, the other saying "i can safely
 receive a temporary". There is nothing 'automatic' about the latter.
The behaviors are strongly related because automatically selecting refness entails dealing between rvalues and lvalues. The most confusing thing would be to choose between: - ref - auto ref - scope ref It is clear to me things could be designed that way. Practically it would be a massive fail because the last two are so closely related, and different in ever subtle ways. I could safely say on Walter and my behalf that we believe such a design would not serve D well at all.
 As I've had to re-iterate countless times, and such is the massive
 fallacy behind all of these threads, this whole debate is NOT about
 lvalues/rvalues, and I wish people would stop using the term 'rvalue' in
 their posts, I worry that they misunderstand the problem every time it's
 said.
It never hurt any of us to entertain the idea that the fallacy may lie within.
 This code is broken:
    void f(ref int x) {}
    int x;
    f(x);
That code is not broken.
 x is an lvalue.
 This is the real problem case, and addressing this will solve the rvalue
 case at the same time.
 Passing an rvalue to a function just generates an implicit temp which is
 functionally identical to the above, except the lifetime of a temp is
 usually the life of the statement rather than the outer scope.
 The problem we need to solve is that of a function being able to safely
 receive a _temporary_.
Oh, you mean an rvalue? :o) Andrei
May 09 2013
prev sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Friday, 10 May 2013 at 00:08:50 UTC, Manu wrote:
 As I've had to re-iterate countless times, and such is the 
 massive fallacy
 behind all of these threads, this whole debate is NOT about
 lvalues/rvalues, and I wish people would stop using the term 
 'rvalue' in
 their posts, I worry that they misunderstand the problem every 
 time it's
 said.

 This code is broken:
   void f(ref int x) {}
   int x;
   f(x);

 x is an lvalue.
There's nothing broken about that code. In what way do you believe it is broken?
 The problem we need to solve is that of a function being able 
 to safely
 receive a _temporary_.
temporary = rvalue
May 10 2013
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, May 10, 2013 10:08:37 Manu wrote:
 Correct, it specifies something _entirely different_, it says "I can safely
 receive a temporary, because I promise not to escape it". This is the
 actual problem that we're trying to solve, and it addresses the problem
 head on.
 
 As I've had to re-iterate countless times, and such is the massive fallacy
 behind all of these threads, this whole debate is NOT about
 lvalues/rvalues, and I wish people would stop using the term 'rvalue' in
 their posts, I worry that they misunderstand the problem every time it's
 said.
 
 This code is broken:
 void f(ref int x) {}
 int x;
 f(x);
 
 x is an lvalue.
 This is the real problem case, and addressing this will solve the rvalue
 case at the same time.
 Passing an rvalue to a function just generates an implicit temp which is
 functionally identical to the above, except the lifetime of a temp is
 usually the life of the statement rather than the outer scope.
 The problem we need to solve is that of a function being able to safely
 receive a _temporary_.
The runtime check for ref that we agreed on already solves the safety problem. So, I see no point in discussing the safety problem further unless there's something wrong with the runtime check solution. And yes, the safety problem is not just a question of rvalues. But whether ref should accept rvalues is very important with regards to being able to write and understand correct and maintainable code. The question of accepting rvalues that we are therefore discussing has _nothing_ to do with safety. It's entirely a question of avoiding other types of bugs - like accepting nonsense like swap(5, 7), which in that case is fortunately obvious but is not obvious in the general case. IMHO, it needs to be clear when a function intends to take an argument by ref because it intends to mutate the argument and when it intends to take an argument by ref because it wants the efficiency boost of avoiding the copy. In the first case, it makes no sense to accept rvalues, and in the second case, you definitely want to accept rvalues. As such, having different syntax is needed (be it auto ref or acceptrvalue or whatever). I'm not entirely against adding a new attribute for that (it would have the added benefit of not needing a compiler optimization to guarantee that a templated function takes its argument by ref when passed an rvalue), but Walter and Andrei don't want to add new attributes if they can avoid it, so I don't expect them to be okay with adding a new attribute. And since auto ref was originally supposed to be this attribute, I'd _much_ rather have that do it than make the mistake of letting ref accept rvalues. - Jonathan M Davis
May 09 2013
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 10 May 2013 10:31, Jonathan M Davis <jmdavisProg gmx.com> wrote:

 On Friday, May 10, 2013 10:08:37 Manu wrote:
 Correct, it specifies something _entirely different_, it says "I can
safely
 receive a temporary, because I promise not to escape it". This is the
 actual problem that we're trying to solve, and it addresses the problem
 head on.

 As I've had to re-iterate countless times, and such is the massive
fallacy
 behind all of these threads, this whole debate is NOT about
 lvalues/rvalues, and I wish people would stop using the term 'rvalue' in
 their posts, I worry that they misunderstand the problem every time it's
 said.

 This code is broken:
 void f(ref int x) {}
 int x;
 f(x);

 x is an lvalue.
 This is the real problem case, and addressing this will solve the rvalue
 case at the same time.
 Passing an rvalue to a function just generates an implicit temp which is
 functionally identical to the above, except the lifetime of a temp is
 usually the life of the statement rather than the outer scope.
 The problem we need to solve is that of a function being able to safely
 receive a _temporary_.
The runtime check for ref that we agreed on already solves the safety problem. So, I see no point in discussing the safety problem further unless there's something wrong with the runtime check solution. And yes, the safety problem is not just a question of rvalues. But whether ref should accept rvalues is very important with regards to being able to write and understand correct and maintainable code. The question of accepting rvalues that we are therefore discussing has _nothing_ to do with safety. It's entirely a question of avoiding other types of bugs - like accepting nonsense like swap(5, 7), which in that case is fortunately obvious but is not obvious in the general case. IMHO, it needs to be clear when a function intends to take an argument by ref because it intends to mutate the argument and when it intends to take an argument by ref because it wants the efficiency boost of avoiding the copy. In the first case, it makes no sense to accept rvalues, and in the second case, you definitely want to accept rvalues. As such, having different syntax is needed (be it auto ref or acceptrvalue or whatever). I'm not entirely against adding a new attribute for that (it would have the added benefit of not needing a compiler optimization to guarantee that a templated function takes its argument by ref when passed an rvalue), but Walter and Andrei don't want to add new attributes if they can avoid it, so I don't expect them to be okay with adding a new attribute. And since auto ref was originally supposed to be this attribute, I'd _much_ rather have that do it than make the mistake of letting ref accept rvalues.
What were the arguments again against ref const()? You're talking about making it clear that the function isn't planning on mutating the given rvalue... I understand that const is stronger than C++, but is it actually a deal-breaker? It's the most logical fit here.
May 09 2013
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 09 May 2013 20:38:47 -0400, Manu <turkeyman gmail.com> wrote:

 What were the arguments again against ref const()? You're talking about
 making it clear that the function isn't planning on mutating the given
 rvalue...
 I understand that const is stronger than C++, but is it actually a
 deal-breaker? It's the most logical fit here.
the counter-argument goes something like this: struct VeryLarge { int[10] buffer; VeryLarge *next; } So let's say you build a VeryLarge and return it, on the stack. Return by value. VeryLarge buildOne(someArguments); OK, you now want to assign it to a property: class X { private VeryLarge _vl; property void vl(ref VeryLarge otherValue) { _vl = otherValue;} } X x = new X; x.vl = buildOne(...); If we make otherValue const, then we can't assign because of the indirection. It's a tenuous argument, and I may not have made it in the best way, but the bottom line is that const is overly restrictive in this case. We're passing by ref because we don't want to incur the copy penalty *twice*. If we make it const, we've added an incorrect restriction. The solution, ironically, is to take VeryLarge by value as an overload. This will simply do a move, and since it's already on the stack, no extra copy is made. So we NEED it to be mutable, and we don't want to restrict ourselves from accepting rvalues. So the above works fine as ref, for r and l values, because we are just trying to copy the data. It's when you specifically are passing by ref to modify the data that you want to reject rvalues. As the original post in this thread pointed out, it's the way a library can alter another author's intention that causes problems. I have another idea, but I need to put it at the top so it's not lost :) -Steve
May 09 2013
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/13 5:55 PM, Manu wrote:
 auto is a template concept, it should not be applied here.
auto x = 5; Where's the template? Andrei
May 09 2013
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/09/2013 09:43 PM, Andrei Alexandrescu wrote:
 On 5/9/13 3:39 PM, Rob T wrote:
 So, if I understand correctly, auto ref for templates will end up doing
 exactly the same thing as auto ref for non-template functions? That
 would be perfect, otherwise it'll be terribly confusing.
There would be clear restrictions on non-template functions, e.g. a non-template cannot return auto ref. Andrei
Returning auto ref is fine as long as no auto ref argument is returned.
May 09 2013
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, May 09, 2013 21:39:14 Rob T wrote:
 So, if I understand correctly, auto ref for templates will end up
 doing exactly the same thing as auto ref for non-template
 functions? That would be perfect, otherwise it'll be terribly
 confusing.
auto ref on templates would be essentially unchanged from what it is now (the fact that it forwards the refness of its arguments is actually very useful for some situations, and we don't want to lose that). However, what we might be able to do as an optimization would be to make it so that auto ref on templates acts the same as auto ref on non-templated functions when the compiler is able to determine that there's no real semantic difference - i.e. when the fact that the refness is being forwarded is irrelevant. So, with a function like auto foo(T)(auto ref T t) { return t; } the non-templated solution could be used, whereas in a function like auto ref foo(T)(auto ref T t) { return t; } or auto foo(T)(auto ref T t) { return anotherFuncWhichTakesByAutoRef(t); } the compiler would have to use the normal templated solution, because the refness of the arguments would have an effect on the code. - Jonathan M Davis
May 09 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/09/2013 11:06 PM, Jonathan M Davis wrote:
 On Thursday, May 09, 2013 21:39:14 Rob T wrote:
 So, if I understand correctly, auto ref for templates will end up
 doing exactly the same thing as auto ref for non-template
 functions? That would be perfect, otherwise it'll be terribly
 confusing.
auto ref on templates would be essentially unchanged from what it is now (the fact that it forwards the refness of its arguments is actually very useful for some situations, and we don't want to lose that). However, what we might be able to do as an optimization would be to make it so that auto ref on templates acts the same as auto ref on non-templated functions when the compiler is able to determine that there's no real semantic difference - i.e. when the fact that the refness is being forwarded is irrelevant. So, with a function like auto foo(T)(auto ref T t) { return t; } the non-templated solution could be used, whereas in a function like auto ref foo(T)(auto ref T t) { return t; } or
Yes.
 auto foo(T)(auto ref T t)
 {
   return anotherFuncWhichTakesByAutoRef(t);
 }

 the compiler would have to use the normal templated solution, because the
 refness of the arguments would have an effect on the code.

 ...
Actually, no. This would instantiate the ref version in any case, hence only one instantiation is necessarily required.
May 09 2013
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On 10 May 2013 05:39, Rob T <alanb ucora.com> wrote:

 On Thursday, 9 May 2013 at 19:26:37 UTC, Jonathan M Davis wrote:

 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
So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.
I don't think this is entirely true, auto ref is a template concept, that is, "automatic ref-ness", it selects the ref-ness of the argument automatically, at compile time, just like auto applied everywhere else (selects a type for instance, at compile time). This concept doesn't make any sense applied to a non-template. It *IS* a ref as specified by the programmer, there's nothing 'automatic' about it. So to say it will do 'exactly the same thing' is a misunderstanding. I argue that 'auto ref' as applied to non-templates will only create confusion, it effectively re-enforces the type of confusion that you have just shown. This is the reasoning for the argument behind scope ref, which to my mind actually makes good sound sense, and should lead people to a proper understanding of what you are actually doing. Considering the key argument against 'scope ref' is that people don't want to require more attributes to make use of it, I don't see how 'auto ref' satisfies this argument either. Thus, I am quite happy happy with 'ref', it can be made safe, satisfies the argument above, and this seems like a very good start that we might actually all agree on.
May 09 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/09/2013 11:35 PM, Manu wrote:
 ...

 I don't think this is entirely true, auto ref is a template concept,
In the current implementation, but not necessarily.
 that is, "automatic ref-ness", it selects the ref-ness of the argument
 automatically, at compile time, just like auto applied everywhere else
 (selects a type for instance, at compile time).
auto was carried over from C and originally stands for local lifetime. It does _not_ mean "apply type deduction here".
 This concept doesn't
 make any sense applied to a non-template. It *IS* a ref as specified by
 the programmer, there's nothing 'automatic' about it.
Most keywords are poorly chosen.
 So to say it will do 'exactly the same thing' is a misunderstanding. I
 argue that 'auto ref' as applied to non-templates will only create
 confusion, it effectively re-enforces the type of confusion that you
 have just shown.

 This is the reasoning for the argument behind scope ref, which to my
 mind actually makes good sound sense, and should lead people to a proper
 understanding of what you are actually doing.
 Considering the key argument against 'scope ref' is that people don't
 want to require more attributes to make use of it,
This is inaccurate.
 I don't see how 'auto ref' satisfies this argument either.
Sure, it wouldn't.
 Thus, I am quite happy happy with 'ref', it can be made safe, satisfies
 the argument above, and this seems like a very good start that we might
 actually all agree on.
It can make code evolution less straightforward.
May 09 2013
parent reply Manu <turkeyman gmail.com> writes:
On 10 May 2013 08:50, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 05/09/2013 11:35 PM, Manu wrote:

 ...


 I don't think this is entirely true, auto ref is a template concept,
In the current implementation, but not necessarily.
It should stay that way. It's reasonable what it does in it's current implementation. that is, "automatic ref-ness", it selects the ref-ness of the argument
 automatically, at compile time, just like auto applied everywhere else
 (selects a type for instance, at compile time).
auto was carried over from C and originally stands for local lifetime. It does _not_ mean "apply type deduction here".
Eh? 'local lifetime' doesn't sound like it has anything to do with 'apply type deduction here' to me; which is what D does. This concept doesn't
 make any sense applied to a non-template. It *IS* a ref as specified by
 the programmer, there's nothing 'automatic' about it.
Most keywords are poorly chosen.
Is this an argument to continue that trend? That said, I don't find this to be particularly true. Most things make reasonable sense. So to say it will do 'exactly the same thing' is a misunderstanding. I
 argue that 'auto ref' as applied to non-templates will only create
 confusion, it effectively re-enforces the type of confusion that you
 have just shown.

 This is the reasoning for the argument behind scope ref, which to my
 mind actually makes good sound sense, and should lead people to a proper
 understanding of what you are actually doing.
 Considering the key argument against 'scope ref' is that people don't
 want to require more attributes to make use of it,
This is inaccurate.
It's the most consistent argument against scope-ref.
May 09 2013
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 09 May 2013 19:51:47 -0400, Manu <turkeyman gmail.com> wrote:

 On 10 May 2013 08:50, Timon Gehr <timon.gehr gmx.ch> wrote:
 auto was carried over from C and originally stands for local lifetime.  
 It
 does _not_ mean "apply type deduction here".
Eh? 'local lifetime' doesn't sound like it has anything to do with 'apply type deduction here' to me; which is what D does.
auto does not imply type deduction, it is a storage class. D is able to imply type deduction when it knows you are declaring a variable (hence the storage class) and you omit the type. auto actually means 'local' in C. D carries on that tradition, but adds the ability to define the type based on the assignment. These all work: auto x = 1; static x = 1; const x = 1; If the ref storage class could be used in a function/struct, this would work too: ref x = foo(); // assuming foo returns by ref
 Is this an argument to continue that trend?
 That said, I don't find this to be particularly true. Most things make
 reasonable sense.
I think there is no good reason to use auto ref, except that it's already in the book. Any storage class would be fine, and auto ref is going to be super-confusing because it's used elsewhere. Just my opinion. -Steve
May 09 2013
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, May 10, 2013 07:35:36 Manu wrote:
 I don't think this is entirely true, auto ref is a template concept, that
 is, "automatic ref-ness", it selects the ref-ness of the argument
 automatically, at compile time, just like auto applied everywhere else
 (selects a type for instance, at compile time). This concept doesn't make
 any sense applied to a non-template. It *IS* a ref as specified by the
 programmer, there's nothing 'automatic' about it.
I don't buy this at all. The entire point of auto ref on parameters was to say that you wanted to accept both rvalues and lvalues efficiently. The fact that the template implementation happened to forward refness as a result was a happy accident. auto ref is already described in TDPL, and it has nothing to do with templates there. Using auto ref on non-templated functions would be completely in line with what TDPL describes and would implement another feature from TDPL that we're currently missing.
 This is the reasoning for the argument behind scope ref, which to my mind
 actually makes good sound sense, and should lead people to a proper
 understanding of what you are actually doing.
The whole point of making auto ref work with non-templated functions is to be able to say that you want to pass both rvalues and lvalues by ref so that they get passed efficiently. scope says _nothing_ about that. It's all about what escapes the scope, not about how stuff gets passed. So, using scope ref to solve that problem makes no sense, and the changes that we've proposed to make to ref to make it safe pretty much make scope unnecessary. scope ref would be virtually identical to ref given that ref already has to guarantee that the variable being referenced doesn't get destroyed before the ref is. The only real difference would be that scope would presumably additionally prevent doing system stuff like taking the address of the ref. I don't see how this buys us anything. I agree that auto ref isn't a great name, but it's what we already have, and using it on non-templated functions would be using it for exactly what it was designed for in the first place and how it's described in TDPL. The only reason that it's not what we have now is because Walter misunderstood it when he implemented it.
 Considering the key argument against 'scope ref' is that people don't want
 to require more attributes to make use of it, I don't see how 'auto ref'
 satisfies this argument either.
Because we already have auto ref. It just hasn't been implemented on non- templated functions yet even though it was supposed to be. And you were proposing not only using scope ref with a particular meaning on function parameters but also adding it as a return type, which it definitely isn't know regardless of what scope of function parameters does or doesn't do. And it's even questionable that scope as originally intended can be properly implemented anyway.
 Thus, I am quite happy happy with 'ref', it can be made safe, satisfies the
 argument above, and this seems like a very good start that we might
 actually all agree on.
As has already been discussed in this thread, it will introduce maintenance problems if ref accepts rvalues. - Jonathan M Davis
May 09 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/13 6:09 PM, Jonathan M Davis wrote:
 On Friday, May 10, 2013 07:35:36 Manu wrote:
 I don't think this is entirely true, auto ref is a template concept, that
 is, "automatic ref-ness", it selects the ref-ness of the argument
 automatically, at compile time, just like auto applied everywhere else
 (selects a type for instance, at compile time). This concept doesn't make
 any sense applied to a non-template. It *IS* a ref as specified by the
 programmer, there's nothing 'automatic' about it.
I don't buy this at all. The entire point of auto ref on parameters was to say that you wanted to accept both rvalues and lvalues efficiently. The fact that the template implementation happened to forward refness as a result was a happy accident. auto ref is already described in TDPL, and it has nothing to do with templates there. Using auto ref on non-templated functions would be completely in line with what TDPL describes and would implement another feature from TDPL that we're currently missing.
Exactly. I'll add that just saying "auto is a template concept" is really devoid of information. It really is meaningless because "concept" is so broad a word. One may as well say "class is an abstraction notion" or any of a large variety of similar constructs. Andrei
May 09 2013
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 10 May 2013 08:09, Jonathan M Davis <jmdavisProg gmx.com> wrote:

 On Friday, May 10, 2013 07:35:36 Manu wrote:
 I don't think this is entirely true, auto ref is a template concept, that
 is, "automatic ref-ness", it selects the ref-ness of the argument
 automatically, at compile time, just like auto applied everywhere else
 (selects a type for instance, at compile time). This concept doesn't make
 any sense applied to a non-template. It *IS* a ref as specified by the
 programmer, there's nothing 'automatic' about it.
I don't buy this at all. The entire point of auto ref on parameters was to say that you wanted to accept both rvalues and lvalues efficiently. The fact that the template implementation happened to forward refness as a result was a happy accident.
Or perhaps it's actually the core promise that was just poorly understood at the time? auto ref is already described in TDPL, and it has nothing to
 do with templates there. Using auto ref on non-templated functions would be
 completely in line with what TDPL describes and would implement another
 feature from TDPL that we're currently missing.
Making it blindly suit the book, whether that idea was initially right or wrong without having actually tried it out or argued it through is not a compelling argument for my money.
 This is the reasoning for the argument behind scope ref, which to my mind
 actually makes good sound sense, and should lead people to a proper
 understanding of what you are actually doing.
The whole point of making auto ref work with non-templated functions is to be able to say that you want to pass both rvalues and lvalues by ref so that they get passed efficiently.
Efficiency can not be implied by ref-ness. There are lots of factors, and the programmer needs control over which is chosen. auto ref would seem to me as something of a hack that is required when dealing with ref and templates, because ref is not part of the type like C++. auto ref on templates wouldn't exist if ref was part of the type, it would infer ref-ness automatically like C++ does. Even Walter agreed that it felt like a hack for this reason during conversations. scope says _nothing_ about that. It's all about what
 escapes the scope, not about how stuff gets passed.
It says it in a more pure way. The point is that it is made explicit that the argument will not escape the scope it is given, thus, in conjunction with ref, it becomes naturally safe to pass r-values. So, using scope ref to
 solve that problem makes no sense, and the changes that we've proposed to
 make
 to ref to make it  safe pretty much make scope unnecessary.
I agree, that's why I'm also happy with 'ref' alone, but I still feel it doesn't communicate as much information, which is a trivial by contrast. scope ref would be
 virtually identical to ref given that ref already has to guarantee that the
 variable being referenced doesn't get destroyed before the ref is.
No, there are other bonuses: - It mechanically enforces a given argument will not have a pointer taken and escape. - It gives the extra information to the programmer who can better reason about API intent. - It allows 'ref' alone to retain an important function where it may escape a pointer if it wants to.
 The only
 real difference would be that scope would presumably additionally prevent
 doing
  system stuff like taking the address of the ref. I don't see how this
 buys us
 anything.
Yes this is an advantage, I listed it above. It buys the programmer some additional flexibility/choice. I agree that auto ref isn't a great name, but it's what we already have, and
 using it on non-templated functions would be using it for exactly what it
 was
 designed for in the first place and how it's described in TDPL.
I'm not going to change my position that it makes no sense, and is misleading/confusing without some real arguments, which nobody seems able to provide. auto ref has already shown to create misunderstanding in the minds of non-super-technical programmers. Syntax should encourage correct understanding. The only reason
 that it's not what we have now is because Walter misunderstood it when he
 implemented it.
If that's true, I'm glad he did. Or perhaps he never agreed to it in the first place. His misunderstanding suggests that he never actually agreed to the initial proposal that you refer to.
 Considering the key argument against 'scope ref' is that people don't want
 to require more attributes to make use of it, I don't see how 'auto ref'
 satisfies this argument either.
Because we already have auto ref. It just hasn't been implemented on non- templated functions yet even though it was supposed to be.
...so, we don't have it? And you were
 proposing not only using scope ref with a particular meaning on function
 parameters but also adding it as a return type, which it definitely isn't
 know
 regardless of what scope of function parameters does or doesn't do.
I'm (note: not originally my proposal, although I shared the thought) proposing that scope has a uniform meaning as applied to any variable anywhere, where it's a local, or an argument (just a local declared in a different spot), or a return value (yet another local, declared in a different spot), the meaning is consistent. And it's
 even questionable that scope as originally intended can be properly
 implemented anyway.
...so, the problem is no different than 'auto ref' as you mention above. It's not implemented as drafted, and we're debating what's actually correct. Clearly the draft was incomplete in both cases. I only support the proposal (from others) that scope ref makes so much more sense, and I think we've also proven it can be made to work syntactically without holes, which I don't believe is so for auto ref.
 Thus, I am quite happy happy with 'ref', it can be made safe, satisfies
 the
 argument above, and this seems like a very good start that we might
 actually all agree on.
As has already been discussed in this thread, it will introduce maintenance problems if ref accepts rvalues.
I'm not bothered by that personally, but if it's critically important, then we start arguing scope ref again. Otherwise I am happy to accept 'ref' with new added safety.
May 09 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/10/2013 12:42 AM, Manu wrote:
 On 10 May 2013 08:09, Jonathan M Davis <jmdavisProg gmx.com
 <mailto:jmdavisProg gmx.com>> wrote:
 ...
     So, using scope ref to
     solve that problem makes no sense, and the changes that we've
     proposed to make
     to ref to make it  safe pretty much make scope unnecessary.


 I agree, that's why I'm also happy with 'ref' alone, but I still feel it
 doesn't communicate as much information, which is a trivial by contrast.

     scope ref would be
     virtually identical to ref given that ref already has to guarantee
     that the
     variable being referenced doesn't get destroyed before the ref is.


 No, there are other bonuses:
   - It mechanically enforces a given argument will not have a pointer
 taken and escape.
This is the same for 'ref' in safe code in the final implementation.
   - It gives the extra information to the programmer who can better
 reason about API intent.
No. scope is supposed to restrict escaping (in some way that is still to be determined). If it is overloaded to also mean 'accept rvalues', then reasoning about API intent is actually harmed, because it will not clear whether 'scope' was added to restrict escaping alone or also to accept rvalues.
   - It allows 'ref' alone to retain an important function where it may
 escape a pointer if it wants to.
In safe code? No way.
     The only
     real difference would be that scope would presumably additionally
     prevent doing
      system stuff like taking the address of the ref. I don't see how
     this buys us
     anything.


 Yes this is an advantage, I listed it above. It buys the programmer some
 additional flexibility/choice.

     I agree that auto ref isn't a great name, but it's what we already
     have, and
     using it on non-templated functions would be using it for exactly
     what it was
     designed for in the first place and how it's described in TDPL.


 I'm not going to change my position that it makes no sense, and is
 misleading/confusing without some real arguments, which nobody seems
 able to provide.
 auto ref has already shown to create misunderstanding in the minds of
 non-super-technical programmers.
I think that this should be an oxymoron.
 Syntax should encourage correct understanding.
It can't. FWIW overloading scope fails this requirement badly.
 ...

     As has already been discussed in this thread, it will introduce
     maintenance
     problems if ref accepts rvalues.


 I'm not bothered by that personally, but if it's critically important,
 then we start arguing scope ref again. Otherwise I am happy to accept
 'ref' with new added safety.
Either scope ref will turn out to be liable to similar issues, or a keyword will have been wasted.
May 09 2013
parent Manu <turkeyman gmail.com> writes:
On 10 May 2013 09:09, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 05/10/2013 12:42 AM, Manu wrote:

 On 10 May 2013 08:09, Jonathan M Davis <jmdavisProg gmx.com
 <mailto:jmdavisProg gmx.com>> wrote:
 ...

     So, using scope ref to
     solve that problem makes no sense, and the changes that we've
     proposed to make
     to ref to make it  safe pretty much make scope unnecessary.


 I agree, that's why I'm also happy with 'ref' alone, but I still feel it
 doesn't communicate as much information, which is a trivial by contrast.

     scope ref would be
     virtually identical to ref given that ref already has to guarantee
     that the
     variable being referenced doesn't get destroyed before the ref is.


 No, there are other bonuses:
   - It mechanically enforces a given argument will not have a pointer
 taken and escape.
This is the same for 'ref' in safe code in the final implementation.
Fine. safe is expected to place more restrictions. But it's still something that people need to do sometimes in un- safe code. - It gives the extra information to the programmer who can better
 reason about API intent.
No. scope is supposed to restrict escaping (in some way that is still to be determined). If it is overloaded to also mean 'accept rvalues', then reasoning about API intent is actually harmed, because it will not clear whether 'scope' was added to restrict escaping alone or also to accept rvalues.
The fact that it can safely receive a temporary is implicit if there is a guarantee that it will not escape. Why should explicit syntax exist to say "I accept rvalues", and not "I can safely receive temporaries"? The rvalue case is a subset, and I see no reason for it to receive special treatment. - It allows 'ref' alone to retain an important function where it may
 escape a pointer if it wants to.
In safe code? No way.
Sure, people expect restrictions in safe code. But it's still something that people need to do sometimes in un- safe code. The only
     real difference would be that scope would presumably additionally
     prevent doing
      system stuff like taking the address of the ref. I don't see how
     this buys us
     anything.


 Yes this is an advantage, I listed it above. It buys the programmer some
 additional flexibility/choice.

     I agree that auto ref isn't a great name, but it's what we already
     have, and
     using it on non-templated functions would be using it for exactly
     what it was
     designed for in the first place and how it's described in TDPL.


 I'm not going to change my position that it makes no sense, and is
 misleading/confusing without some real arguments, which nobody seems
 able to provide.
 auto ref has already shown to create misunderstanding in the minds of
 non-super-technical programmers.
I think that this should be an oxymoron.
What? Syntax should encourage correct understanding.

 It can't. FWIW overloading scope fails this requirement badly.
It's not an overload, it's a natural extension of the concept. Using auto is an overload! As has already been discussed in this thread, it will introduce
     maintenance
     problems if ref accepts rvalues.


 I'm not bothered by that personally, but if it's critically important,
 then we start arguing scope ref again. Otherwise I am happy to accept
 'ref' with new added safety.
Either scope ref will turn out to be liable to similar issues, or a keyword will have been wasted.
I don't understand? Can you expand this comment?
May 09 2013
prev sibling parent reply "Rob T" <alanb ucora.com> writes:
On Thursday, 9 May 2013 at 22:42:14 UTC, Manu wrote:
 And it's
 even questionable that scope as originally intended can be 
 properly
 implemented anyway.
...so, the problem is no different than 'auto ref' as you mention above. It's not implemented as drafted, and we're debating what's actually correct. Clearly the draft was incomplete in both cases. I only support the proposal (from others) that scope ref makes so much more sense, and I think we've also proven it can be made to work syntactically without holes, which I don't believe is so for auto ref.
However despite the elusiveness of a solution, it looks like we'll be able to implement auto ref as was originally intended. We may also be able to implement scope as was originally intended, but not if we use it for another purpose. In any event you may want to use scope ref to prevent escapes and also refuse to use rvalues, so it is not a good solution for that reason alone. --rt
May 09 2013
parent reply Manu <turkeyman gmail.com> writes:
On 10 May 2013 09:20, Rob T <alanb ucora.com> wrote:

 On Thursday, 9 May 2013 at 22:42:14 UTC, Manu wrote:

 And it's

 even questionable that scope as originally intended can be properly
 implemented anyway.
...so, the problem is no different than 'auto ref' as you mention above. It's not implemented as drafted, and we're debating what's actually correct. Clearly the draft was incomplete in both cases. I only support the proposal (from others) that scope ref makes so much more sense, and I think we've also proven it can be made to work syntactically without holes, which I don't believe is so for auto ref.
However despite the elusiveness of a solution, it looks like we'll be able to implement auto ref as was originally intended. We may also be able to implement scope as was originally intended, but not if we use it for another purpose.
Except that auto ref as originally intended seems to have been a flawed design, as evidenced by the massive waves this issue keeps creating. the scope ref proposal does not interfere with scope as originally intended, it is a natural extension of the concept... unless I don't understand scope as originally intended properly (which is possible, it's barely documented). In any event you may want to use scope ref to prevent escapes and also
 refuse to use rvalues, so it is not a good solution for that reason alone.
Why? Why would a function want to receive a temporary but not an implicit temporary?
May 09 2013
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/13 8:18 PM, Manu wrote:
 Except that auto ref as originally intended seems to have been a flawed
 design, as evidenced by the massive waves this issue keeps creating.
This is news to me. What is the flaw? Andrei
May 09 2013
prev sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
10-May-2013 04:18, Manu пишет:
 On 10 May 2013 09:20, Rob T <alanb ucora.com <mailto:alanb ucora.com>>
 wrote:

     On Thursday, 9 May 2013 at 22:42:14 UTC, Manu wrote:

         And it's

             even questionable that scope as originally intended can be
             properly
             implemented anyway.


         ...so, the problem is no different than 'auto ref' as you
         mention above.
         It's not implemented as drafted, and we're debating what's actually
         correct. Clearly the draft was incomplete in both cases.
         I only support the proposal (from others) that scope ref makes
         so much more
         sense, and I think we've also proven it can be made to work
         syntactically
         without holes, which I don't believe is so for auto ref.


     However despite the elusiveness of a solution, it looks like we'll
     be able to implement auto ref as was originally intended. We may
     also be able to implement scope as was originally intended, but not
     if we use it for another purpose.


 Except that auto ref as originally intended seems to have been a flawed
 design, as evidenced by the massive waves this issue keeps creating.
Simply put it wasn't ever implemented like it was meant to. When something doesn't exist it's hard to believe that its broken. In fact I expected it to mean what you seem to attribute to scope ref i.e. ~ as C++ const& minus logical const part. The desire to make 2 versions of function in template case is serving one use case only - perfect forwarding and IMO is hacky. Funnily tough this corner-case beast (for templates) is implemented and the chief usage (for normal functions) isn't. -- Dmitry Olshansky
May 10 2013
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
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
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, May 10, 2013 10:38:47 Manu wrote:
 What were the arguments again against ref const()? You're talking about
 making it clear that the function isn't planning on mutating the given
 rvalue...
 I understand that const is stronger than C++, but is it actually a
 deal-breaker? It's the most logical fit here.
Not being able to differentiate between when it's an rvalue and lvalue causes some problems, though Andrei understands those issues far better than I do. I've been able to come up with cases where it would be a problem if you could have ref on a local variable, but without that, I don't really understand where you end up with problems with const ref due to not being able to differentiate between rvalues and lvalues. It's easy to come up with cases with plain ref (e.g. the example in Ali's talk when a type's opAssign treated rvalues and lvalues differently, swapping guts with the rvalue and copying from the lvalue), but I'm not as clear on when it ends up being an issue with const. The fact that we don't allow ref on anything other than parameters and return types really simplifies things, so I'm not sure that the situation with const ref would be anywhere near as bad in D as Andrei thinks that it is in C++, but I don't know. However, regardless of all that, the primary problem that I see with using const ref to indicate that you want to accept rvalues by ref is the fact that D's const is so restrictive, and there are types where const just plain doesn't work for them. As such, it strikes me as a bad idea to require const in order to accept rvalues by ref even if there are no other problems with it. Now, you could end up with other weird problems if you used auto ref (assuming that we used auto ref for this) without const and the function _did_ mutate its argument (we already have that problem with auto ref and templates), but if you really want the extra protection and can afford it, you can always use const with it. The problem is the cases where you can't use const. We've been trying very hard to make it so that const is completely optional precisely because of how restrictive it is. Requiring it for this seems problematic to me. - Jonathan M Davis
May 09 2013
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 05 May 2013 01:49:42 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 2. Code evolution.

 Jonathan mentioned this too. The problem here is that as code evolves,  
 meaningful code doing real work becomes silently useless code that  
 patently does nothing. Consider:

 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]);
    }
 }

 As design evolves, Collection's opIndex may change to return a T instead  
 of ref T (e.g. certain implementations of sparse vectors). When that  
 happens, the caller code will continue to compile and run. However, it  
 won't do anything interesting: fix will be always called against a  
 temporary plucked from the collection.
What about specifying ref at the call site when you know you want the data modified? fix(ref c[i]); Then if c decides to start returning by value, this is a compiler error. IMO, fix really should take a pointer. But we want to avoid pointers due to the danger of them. so this is like applying & but keeps it safe. -Steve
May 09 2013
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, May 09, 2013 21:56:53 Steven Schveighoffer wrote:
 What about specifying ref at the call site when you know you want the data
 modified?
 
 fix(ref c[i]);
 
 Then if c decides to start returning by value, this is a compiler error.
 
 IMO, fix really should take a pointer. But we want to avoid pointers due
 to the danger of them.
 
 so this is like applying & but keeps it safe.
That would be great except for UFCS. How would you designate the ref when it's the first argument? And I think that it's worse to have ref optional at the callsite than to not have it at all. If it weren't for UFCS, I probably would be in favor of requiring it at the way to deal with that, since it does have some sort of UFCS, and it does require ref at the callsite (at least from what I understand - I haven't used - Jonathan M Davis
May 09 2013
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 09 May 2013 22:10:22 -0400, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Thursday, May 09, 2013 21:56:53 Steven Schveighoffer wrote:
 What about specifying ref at the call site when you know you want the  
 data
 modified?

 fix(ref c[i]);

 Then if c decides to start returning by value, this is a compiler error.

 IMO, fix really should take a pointer. But we want to avoid pointers due
 to the danger of them.

 so this is like applying & but keeps it safe.
That would be great except for UFCS. How would you designate the ref when it's the first argument? And I think that it's worse to have ref optional at the callsite than to not have it at all.
1. using UFCS is optional. c[i].fix() works as well as fix(c[i]). 2. We could say UFCS has an implicit ref-at-callsite if needed, and will not compile for rvalues. If you think about it, 'this' is implicitly ref, and UFCS is calling like the first argument is the 'this' reference.
 If it weren't for UFCS, I probably would be in favor of requiring it at  
 the

 has a
 way to deal with that, since it does have some sort of UFCS, and it does
 require ref at the callsite (at least from what I understand - I haven't  
 used

was self-documenting. But I don't think it's something we could add as a requirement, too much code uses ref. I don't think it is bad to make it optional. The caller can put restrictions he desires on types just like the callee can. Consider that the author of fix could change his code to double fix(double), and now the code also is useless. We can't possibly guard against that, because lvalues also bind to non-ref. I tend to feel that any modification that adds or removes ref is a severe breaking change, and is so bad that it shouldn't be done without also changing the name of the function anyway. -Steve
May 09 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, May 09, 2013 22:33:09 Steven Schveighoffer wrote:
 On Thu, 09 May 2013 22:10:22 -0400, Jonathan M Davis <jmdavisProg gmx.com>
 That would be great except for UFCS. How would you designate the ref
 when it's
 the first argument? And I think that it's worse to have ref optional at
 the
 callsite than to not have it at all.
1. using UFCS is optional. c[i].fix() works as well as fix(c[i]).
That would be an option, but as I explain belong, I think that having ref be optional at the call site is a bad idea.
 2. We could say UFCS has an implicit ref-at-callsite if needed, and will
 not compile for rvalues.  If you think about it, 'this' is implicitly ref,
 and UFCS is calling like the first argument is the 'this' reference.
That would make using UFCS with ranges generally illegal, as most range-based funtions do not have ref parameters, and you generally don't want them to be passed by ref.
 If it weren't for UFCS, I probably would be in favor of requiring it at
 the

 has a
 way to deal with that, since it does have some sort of UFCS, and it does
 require ref at the callsite (at least from what I understand - I haven't
 used

was self-documenting. But I don't think it's something we could add as a requirement, too much code uses ref. I don't think it is bad to make it optional.
This has come up on the newsgroup before, and I really do think that having it as optional is worse than not having it. If it's required, then when you see that an argument is marked with ref, you know that the parameter is a ref parameter, and when you see an argument without ref, you know that the parameter is not a ref parameter. And the compiler enforces both, so you always catch it when the function changes. On the other hand, if ref at the callsite is optional, then the lack of ref at the call site means absolutely nothing. You're still going to have to look at every single function signature to determine if it takes a ref argument or not, meaning that the lack of ref at the call site buys you nothing at best and harms you at worst, because it gives you a false sense of security that the argument is not being passed by ref. Programmers would have to be 100% consistent in using ref at the callsite to avoid that, and that's obviously not going to happen. You _do_ still get the gain that if you use ref at the call site, then your code will break if the parameter is no longer ref. But I think that the fact that the lack of ref means nothing while still giving the impression that the parameter is not a ref parameter is worse than never having ref at the call site. - Jonathan M Davis
May 09 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 10 May 2013 at 04:44:29 UTC, Jonathan M Davis wrote:
 2. We could say UFCS has an implicit ref-at-callsite if 
 needed, and will
 not compile for rvalues.  If you think about it, 'this' is 
 implicitly ref,
 and UFCS is calling like the first argument is the 'this' 
 reference.
That would make using UFCS with ranges generally illegal, as most range-based funtions do not have ref parameters, and you generally don't want them to be passed by ref.
Why ? Range behavior is undefined when passed by value.
 On the other hand, if ref at the callsite is optional, then the 
 lack of ref at
 the call site means absolutely nothing. You're still going to 
 have to look at
 every single function signature to determine if it takes a ref 
 argument or
 not, meaning that the lack of ref at the call site buys you 
 nothing at best
 and harms you at worst, because it gives you a false sense of 
 security that
 the argument is not being passed by ref. Programmers would have 
 to be 100%
 consistent in using ref at the callsite to avoid that, and 
 that's obviously
 not going to happen.
It make sense when you pass by ref to modify things. In this case passing an rvalue don't make any sense. If you di it for performance, then this is yet another instance of the compiler rewriting things to improve performances.
May 09 2013
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, May 10, 2013 07:01:21 deadalnix wrote:
 On Friday, 10 May 2013 at 04:44:29 UTC, Jonathan M Davis wrote:
 2. We could say UFCS has an implicit ref-at-callsite if
 needed, and will
 not compile for rvalues.  If you think about it, 'this' is
 implicitly ref,
 and UFCS is calling like the first argument is the 'this'
 reference.
That would make using UFCS with ranges generally illegal, as most range-based funtions do not have ref parameters, and you generally don't want them to be passed by ref.
Why ? Range behavior is undefined when passed by value.
In some cases you don't care, but ranges are almost always passed by value, and when you're worried about ranges being reference types, you call save, meaning that if you're chaining, you're even more likely to passing an rvalue. And even if a range is a reference type, it's frequently not an lvalue, because it's frequently the return value of a function, and it's frequently the case that such return values can't be returned by ref, so requiring ref for UFCS would have a tendency to kill function chaining via UFCS.
 On the other hand, if ref at the callsite is optional, then the
 lack of ref at
 the call site means absolutely nothing. You're still going to
 have to look at
 every single function signature to determine if it takes a ref
 argument or
 not, meaning that the lack of ref at the call site buys you
 nothing at best
 and harms you at worst, because it gives you a false sense of
 security that
 the argument is not being passed by ref. Programmers would have
 to be 100%
 consistent in using ref at the callsite to avoid that, and
 that's obviously
 not going to happen.
It make sense when you pass by ref to modify things. In this case passing an rvalue don't make any sense. If you di it for performance, then this is yet another instance of the compiler rewriting things to improve performances.
The only time that it would make sense to put ref at the callsite is if you intend to mutate the object (and the ref at the callsite is then intended to signal that to the programmer as well as get the compiler to verify that it's being passed by ref). If plain ref were to accept rvalues, then ref at the callsite would be pretty much meaningless. And it would never make sense to use it with something like auto ref. - Jonathan M Davis
May 09 2013
prev sibling parent "Dicebot" <m.strashun gmail.com> writes:
On Friday, 10 May 2013 at 05:01:22 UTC, deadalnix wrote:
 Why ? Range behavior is undefined when passed by value.
Ugh, it is usually other way around, at least given current typical range implementations.
May 10 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 10 May 2013 00:44:08 -0400, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Thursday, May 09, 2013 22:33:09 Steven Schveighoffer wrote:
 On Thu, 09 May 2013 22:10:22 -0400, Jonathan M Davis  
 <jmdavisProg gmx.com>
 That would be great except for UFCS. How would you designate the ref
 when it's
 the first argument? And I think that it's worse to have ref optional  
at
 the
 callsite than to not have it at all.
1. using UFCS is optional. c[i].fix() works as well as fix(c[i]).
That would be an option, but as I explain belong, I think that having ref be optional at the call site is a bad idea.
 2. We could say UFCS has an implicit ref-at-callsite if needed, and will
 not compile for rvalues.  If you think about it, 'this' is implicitly  
 ref,
 and UFCS is calling like the first argument is the 'this' reference.
That would make using UFCS with ranges generally illegal, as most range-based funtions do not have ref parameters, and you generally don't want them to be passed by ref.
I misunderstood. You would not need ref at callsite because it's optional. So this actually is fine. You don't need to do anything. How would you prevent 'rvalue' ranges from going in? Don't use UFCS, and put ref at callsite. The problem with this whole rvalue/lvalue scheme is that rvalue and lvalue are not good terms to describe what we want. Just because you can't assign to an rvalue doesn't mean you can't assign to a member of that rvalue, it could be a pointer.
 This has come up on the newsgroup before, and I really do think that  
 having it
 as optional is worse than not having it. If it's required, then when you  
 see
 that an argument is marked with ref, you know that the parameter is a ref
 parameter, and when you see an argument without ref, you know that the
 parameter is not a ref parameter. And the compiler enforces both, so you
 always catch it when the function changes.
That is not the point of this suggestion. It would simply be to ensure changes from an lvalue to an rvalue don't compile. It would only be used in those cases. In other words, ref at callsite means "I have an lvalue". If that changes, no compilation. -Steve
May 10 2013