www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - ref const(T) the same as C++'s const T&?

reply Peter Alexander <peter.alexander.au gmail.com> writes:
Do D const references work the same as C++'s?

i.e.
- Can they bind to rvalues?
- Do they extend the life of rvalues?

If they do, are there any differences from C++?

If they don't, how do I pass large structs into a function efficiently?

Thanks
Feb 15 2011
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, February 15, 2011 11:48:25 Peter Alexander wrote:
 Do D const references work the same as C++'s?
 
 i.e.
 - Can they bind to rvalues?
 - Do they extend the life of rvalues?
 
 If they do, are there any differences from C++?
 
 If they don't, how do I pass large structs into a function efficiently?

The only bind to lvalues. A ref is a reference to a variable, so normal ref has to be passed a variable. In C++, it was decided that const& was special and could take a temporary. In D, it's no different from ref except that it's const, so it can't be changed. There have been requests to fix this, but Andrei is adamant that it was a huge mistake in C++ to do it since it makes it so that you can't determine whether a const& is really a rvalue or an lvalue. I don't know enough to know how much that really matters, but apparently it causes problems. The result is that D doesn't do that, which can be rather annoying when trying to pass temporaries to functions which take const ref, but that's the way it goes. If you want to pass a large struct to a function efficiently, and you know that function isn't going to need to alter the struct, make it const ref. That way, it's const, and it doesn't get copied. However, that means that you can't pass temporaries to it. However, if you need to pass a temporary, then you're going to have to actually assign it to a variable first. I _thought_ tht you could define a cost ref version and a non-const ref version and it would call the const ref version on lvalues and the non-const ref version on rvalues, but when I just tried it, it seems to call the non-const ref version in both cases. I don't know if that's a bug or not. But if you just have a const ref version, and you create a variable where you'd normally use a temporary, you'll be fine. - Jonathan M Davis
Feb 15 2011
parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 15/02/11 7:55 PM, Jonathan M Davis wrote:
 On Tuesday, February 15, 2011 11:48:25 Peter Alexander wrote:
 Do D const references work the same as C++'s?

 i.e.
 - Can they bind to rvalues?
 - Do they extend the life of rvalues?

 If they do, are there any differences from C++?

 If they don't, how do I pass large structs into a function efficiently?

The only bind to lvalues. A ref is a reference to a variable, so normal ref has to be passed a variable. In C++, it was decided that const& was special and could take a temporary. In D, it's no different from ref except that it's const, so it can't be changed. There have been requests to fix this, but Andrei is adamant that it was a huge mistake in C++ to do it since it makes it so that you can't determine whether a const& is really a rvalue or an lvalue. I don't know enough to know how much that really matters, but apparently it causes problems. The result is that D doesn't do that, which can be rather annoying when trying to pass temporaries to functions which take const ref, but that's the way it goes. If you want to pass a large struct to a function efficiently, and you know that function isn't going to need to alter the struct, make it const ref. That way, it's const, and it doesn't get copied. However, that means that you can't pass temporaries to it. However, if you need to pass a temporary, then you're going to have to actually assign it to a variable first. I _thought_ tht you could define a cost ref version and a non-const ref version and it would call the const ref version on lvalues and the non-const ref version on rvalues, but when I just tried it, it seems to call the non-const ref version in both cases. I don't know if that's a bug or not. But if you just have a const ref version, and you create a variable where you'd normally use a temporary, you'll be fine. - Jonathan M Davis

Surely this is a major usability issue? I have to create a new local variable just so that I can pass structs round efficiently (either that or change the struct to have COW or ref semantics, both of which are unacceptable).
Feb 15 2011
parent reply Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
Jonathan M Davis wrote:

...
 
 Personally, it wouldn't hurt my feelings any to have const ref take
 temporaries. I do not understand why it's a problem. But Andrei insists
 that it is. Presumably Walter agrees, but I don't know. They could very
 well be right and that it's overall better _not_ to have const ref take
 temporaries, but it _is_ annoying. Since I don't understand what the real
 problem with not knowing whether const ref is actually referring to an
 lvalue or rvalue is, I can't really judge whether they're right or wrong.
 However, Andrei is certain that it's on of C++'s biggest mistakes.
 
 Regardless, the general push has been that structs be cheap to copy, and I
 would argue that if you're structs _aren't_ relatively cheap to copy, you
 should at least consider rethinking your design. Sometimes COW or ref
 semantics will probably be required though.
 
 There may be a way to solve this problem reasonably and still have const
 ref require lvalues, but for the moment, we have to deal with it.
 
 - Jonathan M Davis

For reference, here is a link to the thread discussing it: http://www.mail- archive.com/digitalmars-d puremagic.com/msg44075.html If I understood that discussion correctly, 'auto ref' is supposed to solve the rvalue references problem but are not completely implemented yet.
Feb 15 2011
parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 15/02/11 10:47 PM, Lutger Blijdestijn wrote:
 For reference, here is a link to the thread discussing it: http://www.mail-
 archive.com/digitalmars-d puremagic.com/msg44075.html

 If I understood that discussion correctly, 'auto ref' is supposed to solve
 the rvalue references problem but are not completely implemented yet.

Thanks Lutger. From the thread, it seems like the correct annotation would be 'auto ref const T', would it not?
Feb 15 2011
parent Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
Peter Alexander wrote:

 On 15/02/11 10:47 PM, Lutger Blijdestijn wrote:
 For reference, here is a link to the thread discussing it:
 http://www.mail- archive.com/digitalmars-d puremagic.com/msg44075.html

 If I understood that discussion correctly, 'auto ref' is supposed to
 solve the rvalue references problem but are not completely implemented
 yet.

Thanks Lutger. From the thread, it seems like the correct annotation would be 'auto ref const T', would it not?

Yes, I think so.
Feb 15 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, February 15, 2011 12:35:31 Peter Alexander wrote:
 On 15/02/11 7:55 PM, Jonathan M Davis wrote:
 On Tuesday, February 15, 2011 11:48:25 Peter Alexander wrote:
 Do D const references work the same as C++'s?
 
 i.e.
 - Can they bind to rvalues?
 - Do they extend the life of rvalues?
 
 If they do, are there any differences from C++?
 
 If they don't, how do I pass large structs into a function efficiently?

The only bind to lvalues. A ref is a reference to a variable, so normal ref has to be passed a variable. In C++, it was decided that const& was special and could take a temporary. In D, it's no different from ref except that it's const, so it can't be changed. There have been requests to fix this, but Andrei is adamant that it was a huge mistake in C++ to do it since it makes it so that you can't determine whether a const& is really a rvalue or an lvalue. I don't know enough to know how much that really matters, but apparently it causes problems. The result is that D doesn't do that, which can be rather annoying when trying to pass temporaries to functions which take const ref, but that's the way it goes. If you want to pass a large struct to a function efficiently, and you know that function isn't going to need to alter the struct, make it const ref. That way, it's const, and it doesn't get copied. However, that means that you can't pass temporaries to it. However, if you need to pass a temporary, then you're going to have to actually assign it to a variable first. I _thought_ tht you could define a cost ref version and a non-const ref version and it would call the const ref version on lvalues and the non-const ref version on rvalues, but when I just tried it, it seems to call the non-const ref version in both cases. I don't know if that's a bug or not. But if you just have a const ref version, and you create a variable where you'd normally use a temporary, you'll be fine. - Jonathan M Davis

Surely this is a major usability issue? I have to create a new local variable just so that I can pass structs round efficiently (either that or change the struct to have COW or ref semantics, both of which are unacceptable).

Well, if it's made so that you can have const ref and non-const ref version and the const ref gets called for lvalues and the non-const ref gets called for rvalues, then that fixes most of the problem, though it's still annoying. e.g. int func(const ref S s) { //... } int func(S s) { return func(s); } At the moment, the const ref version only gets called if the struct you're passing in is actually const, so that idiom doesn't currently work. I'm not sure whether it's actually supposed to or not though. I would hope so, but I don't know. Personally, it wouldn't hurt my feelings any to have const ref take temporaries. I do not understand why it's a problem. But Andrei insists that it is. Presumably Walter agrees, but I don't know. They could very well be right and that it's overall better _not_ to have const ref take temporaries, but it _is_ annoying. Since I don't understand what the real problem with not knowing whether const ref is actually referring to an lvalue or rvalue is, I can't really judge whether they're right or wrong. However, Andrei is certain that it's on of C++'s biggest mistakes. Regardless, the general push has been that structs be cheap to copy, and I would argue that if you're structs _aren't_ relatively cheap to copy, you should at least consider rethinking your design. Sometimes COW or ref semantics will probably be required though. There may be a way to solve this problem reasonably and still have const ref require lvalues, but for the moment, we have to deal with it. - Jonathan M Davis
Feb 15 2011
prev sibling parent Trass3r <un known.com> writes:
Peter Alexander Wrote:
 If they don't, how do I pass large structs into a function efficiently?

The weird thing is struct literals count as lvalues in D2, so this works: struct A {} void foo(ref A a) {} void main() { foo(A()); } while calling the following doesn't: static A bar() { return A(); } foo(bar()); Where's the freaking difference?
Mar 01 2011