March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Saturday, 2 March 2013 at 01:15:20 UTC, Steven Schveighoffer wrote: > The point is simple: > > foo(ref M m) > foo(M m) > > An lvalue, yes, we want that to bind to ref. > > But an rvalue? I want that to bind to foo(M m), otherwise I would not have added that method. The by-value version is *more efficient* than the by-ref version with rvalues, even for large structs. > > But what if I ALSO want to say that foo doesn't change m? Well, that's easy! I just do: > > foo(const ref M m) > > But this makes rvalues bind to that version too! This is the problem. I want it to be ref, to avoid copies of a large struct, and I want it to be const, for contract purposes, I DIDN'T want it to accept rvalues. I know, that's what 'auto ref' would be for. > The point is, there is a legitimate reason to Mark a parameter const ref BESIDES wanting to have it bind to rvalues. I'm aware of that, sorry if I gave the wrong impression, but what I see in most of my cases is what I was referring to. We just got a similar problem where you have mutable data and immutable date, where there we have const as a middle ground that accepts both. We don't currently have the same thing for referencing. There are important reasons for having something referenced and having them as Rvalues, but when you don't care or need the middle ground is missing here. > In my code base, I have actual comments that explain why I have ordered certain operations the way I did! > > But we have more problems than just rvalue references. The compiler doesn't "see through" structs to know whether something is an lvalue or an rvalue. > > Consider writing your own pointer type: > > struct T > { > int *x; > void opUnary(string op)() if (op == "++") {++(*x);} > } > > int x; > > T foo() > { > return T(&x); > } > > void main() > { > auto t = foo; > t++; // ok > foo++; // Error: foo() is not an lvalue > } > > That should not be an error, or I should at least be able to tell the compiler "this is NOT an rvalue, even if it seems like one". No, foo returns an lValue, however the ++ is likely wrong. The compiler could have an ingrained belief that ++ is an assignment operator meaning it requires an Lvalue, at which point the code is wrong. But ++ is just an operator for the struct, so I don't know. |
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Era Scarecrow | On Fri, 01 Mar 2013 20:37:26 -0500, Era Scarecrow <rtcvb32@yahoo.com> wrote:
> On Saturday, 2 March 2013 at 01:15:20 UTC, Steven Schveighoffer wrote:
>> void main()
>> {
>> auto t = foo;
>> t++; // ok
>> foo++; // Error: foo() is not an lvalue
>> }
>>
>> That should not be an error, or I should at least be able to tell the compiler "this is NOT an rvalue, even if it seems like one".
>
> No, foo returns an lValue, however the ++ is likely wrong. The compiler could have an ingrained belief that ++ is an assignment operator meaning it requires an Lvalue, at which point the code is wrong. But ++ is just an operator for the struct, so I don't know.
You are saying opposite things "foo returns an LValue" "it requires an Lvalue, at which point the code is wrong"
Which is it?
I contend that T is an LValue type. If the compiler can't see that, it needs to be told, or it needs to get out of the way.
I just tested a simple "incx()" function that is identical to the operator, and it works. I can even call the operator via foo.opUnary!"++"() and it works! So the compiler is restricting things that are trivially circumvented.
Maybe it should be: calling a method of an rvalue, even if that method is called via operator overload, should always be allowed. Any simple field access should fail if the rvalue is modified. So for instance foo.x++ should fail, but foo++ should work, even if all it does is x++. Maybe if the compiler inlines the code and sees it trivially reduces to a rejected case, it can reject it.
-Steve
|
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Saturday, 2 March 2013 at 01:51:28 UTC, Steven Schveighoffer wrote: > On Fri, 01 Mar 2013 20:37:26 -0500, Era Scarecrow <rtcvb32@yahoo.com> wrote: > >> On Saturday, 2 March 2013 at 01:15:20 UTC, Steven Schveighoffer wrote: >>> void main() >>> { >>> auto t = foo; >>> t++; // ok >>> foo++; // Error: foo() is not an lvalue >>> } >>> >>> That should not be an error, or I should at least be able to tell the compiler "this is NOT an rvalue, even if it seems like one". >> >> No, foo returns an lValue, however the ++ is likely wrong. The compiler could have an ingrained belief that ++ is an assignment operator meaning it requires an Lvalue, at which point the code is wrong. But ++ is just an operator for the struct, so I don't know. > > You are saying opposite things "foo returns an LValue" "it requires an Lvalue, at which point the code is wrong" Sorry I hate typos. foo returns an Rvalue, therefore is an Rvalue. > I contend that T is an LValue type. If the compiler can't see that, it needs to be told, or it needs to get out of the way. > > I just tested a simple "incx()" function that is identical to the operator, and it works. I can even call the operator via foo.opUnary!"++"() and it works! So the compiler is restricting things that are trivially circumvented. Anyways, because of how ++ and -- are assumed to basically also be opAssign it requires an LValue, at least that's what the compiler sees, and for simplicity it should probably continue to follow that. Calling it explicitly removes the assumption it requires an Lvalue. If it's const then calling it could be safe as the object/temporary/Rvalue never changes. Hard to say. > Maybe it should be: calling a method of an rvalue, even if that method is called via operator overload, should always be allowed. Any simple field access should fail if the rvalue is modified. So for instance foo.x++ should fail, but foo++ should work, even if all it does is x++. Maybe if the compiler inlines the code and sees it trivially reduces to a rejected case, it can reject it. |
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Era Scarecrow | Era Scarecrow: Did I understand right that your answer is 'No, we don't need something like const&'? |
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Era Scarecrow | On Saturday, 2 March 2013 at 00:49:55 UTC, Era Scarecrow wrote:
> If 'auto ref' gets accepted for non-template functions, it goes away. With M as you show, returning ref doesn't work so that example I was going to suggest doesn't work.
auto ref will in all probability never work for non-template functions, as I said before.
Steven Schveighoffer:
I understand, that you like to have rvalue references for structs but I still don't understand why.
Despite yesterday's discussion, I still do not understand the reason to use a struct instead of a class, when it comes to a massive amount of data.
structs are copied or moved, structs have no polymorphism and no interfaces, so why you should use a struct instead of a class, if your struct is very massive? I don't get it.
|
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Namespace | On Saturday, 2 March 2013 at 08:56:04 UTC, Namespace wrote:
> On Saturday, 2 March 2013 at 00:49:55 UTC, Era Scarecrow wrote:
>> If 'auto ref' gets accepted for non-template functions, it goes away. With M as you show, returning ref doesn't work so that example I was going to suggest doesn't work.
>
> auto ref will in all probability never work for non-template functions, as I said before.
>
> Steven Schveighoffer:
> I understand, that you like to have rvalue references for structs but I still don't understand why.
> Despite yesterday's discussion, I still do not understand the reason to use a struct instead of a class, when it comes to a massive amount of data.
> structs are copied or moved, structs have no polymorphism and no interfaces, so why you should use a struct instead of a class, if your struct is very massive? I don't get it.
1/ Generic code. You may not know that the data are big after starting conglomerating more and more stuff.
2/ Data may be small, but with an expensive copy mecanism.
3/ A class would require to create the proper routines to duplicate itself.
4/ Classes imply indirections. Which may be a problem (think about array of such item for instance).
5/ Value type have great benefice in regard to the GC. LRU cache is a very good way to kill Java's GC, because it generate plenty of old garbages. The same thing in C# using value types don't cause that much GC trashing.
And I can go on and on.
|
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | > 1/ Generic code. You may not know that the data are big after starting conglomerating more and more stuff. auto ref for templates still exists and won't get away. > 2/ Data may be small, but with an expensive copy mecanism. Example? > And I can go on and on. Yes go on, I'd like to hear more. |
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Namespace | On Saturday, 2 March 2013 at 09:31:56 UTC, Namespace wrote: >> 1/ Generic code. You may not know that the data are big after starting conglomerating more and more stuff. > auto ref for templates still exists and won't get away. > Passing by ref small struct for nothing is also a performance concern. You'll access them via dereference when you could have them directly in registers, and reduce the compiler capability of doing optimization based on aliasing. Auto ref is convenient, but look more like an ugly patch than a real solution. >> 2/ Data may be small, but with an expensive copy mecanism. > Example? > struct ValueArray(T) { T[] data; alias this = data; // Damned, it is broken on 2.062 this(this) { data = data.dup; } } >> And I can go on and on. > Yes go on, I'd like to hear more. I don't see the point of providing more data when you ignored half of what I provided and misunderstood the other half. |
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | Excuse me if I misunderstood you, this happens, especially if English is not the native language. But you aren't talking only with me, but I want to hear your full opinion. And if I ignore something, then I have nothing say, because I have no arguments. Say you can take it as tacit consent. |
March 02, 2013 Re: Does D really need something like const&? | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Saturday, 2 March 2013 at 08:29:25 UTC, Namespace wrote: > Era Scarecrow: > Did I understand right that your answer is 'No, we don't need something like const&'? const& is ugly and suggests it is using a pointer which we don't want to use except in low level stuff. const& doesn't make sense, but 'auto ref' does, and I think it could be quite useful. On Saturday, 2 March 2013 at 09:38:17 UTC, deadalnix wrote: > On Saturday, 2 March 2013 at 09:31:56 UTC, Namespace wrote: >> On Saturday, 2 March 2013 at 09:38:17 UTC, deadalnix wrote: >>> 1/ Generic code. You may not know that the data are big after starting conglomerating more and more stuff. >> auto ref for templates still exists and won't get away. > > Passing by ref small struct for nothing is also a performance concern. You'll access them via dereference when you could have them directly in registers, and reduce the compiler capability of doing optimization based on aliasing. > > Auto ref is convenient, but look more like an ugly patch than a real solution. And if it's not implemented I'll be doomed making the same forwarding functions due to ref & const preference rules. I can live with it but I don't want to. I'm not saying I'll use auto ref for everything, only when it makes sense to. >>> 2/ Data may be small, but with an expensive copy mechanism. >> Example? > > struct ValueArray(T) { > T[] data; > alias this = data; // Damned, it is broken on 2.062 > > this(this) { > data = data.dup; > } > } Perhaps enlarge the example just a touch... alias ValueArray!ubyte BigUbArray; BigUbArray array = new ubyte[1<<20]; //1 meg static assert(BigUbArray.sizeof <= 16); //gee 'BigUbArray' is only 8-16 bytes!!! postblit! void func(BigUbArray s); If it's POD however then it shouldn't have any modifications/postblit or anything special to worry about. |
Copyright © 1999-2021 by the D Language Foundation