Thread overview | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
December 18, 2016 meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Reading std/bigint.d code: https://github.com/dlang/phobos/blob/00c1cc3b0d354363793c8b419ce84da722578138/std/bigint.d#L589 I have seen this: bool opEquals()(auto ref const BigInt y) const pure @nogc { return sign == y.sign && y.data == data; } my problem is that I do not understand the role/meaning of "auto" in this context. Moreover in the opCmp code, "auto" is not present anymore, which is an extra source of confusions for me. int opCmp(ref const BigInt y) pure nothrow @nogc const { // Simply redirect to the "real" opCmp implementation. return this.opCmp!BigInt(y); } What is the rational? ----------------- Another interrogation for me, who come from C++, is how to translate into D: template<typename T> void foo(T&& t); |
December 18, 2016 Re: meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Picaud Vincent | On Sunday, 18 December 2016 at 13:14:08 UTC, Picaud Vincent wrote: > Reading std/bigint.d code: > > https://github.com/dlang/phobos/blob/00c1cc3b0d354363793c8b419ce84da722578138/std/bigint.d#L589 > > I have seen this: > > bool opEquals()(auto ref const BigInt y) const pure @nogc > { > return sign == y.sign && y.data == data; > } > > my problem is that I do not understand the role/meaning of "auto" in this context. With auto ref, the parameter can be either a LValue or a RValue. When passing a struct as auto ref, it's taken as ref. for example: struct Foo{this(this){writeln("copy");}} struct Bar{@disable this(this);} void testAsRef(T)(ref T t){} void testAsValue(T)(T t){} void testRefOrValue(T)(auto ref T t){} Foo foo; Bar bar; testAsRef(1); // error 1 is not ref testAsRef(foo); // ok, not copied testAsRef(bar); // ok, not copied testAsValue(1); // ok testAsValue(foo); // ok but copied testAsValue(bar); // error, could only be copied but postblit is disabled testRefOrValue(1); // ok, not taken as ref testRefOrValue(foo); // ok, not copied testRefOrValue(bar); // ok, taken as ref As you can see, auto ref is more flexible with the parameter. This make sense for templated functions. |
December 18, 2016 Re: meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Picaud Vincent | On Sunday, 18 December 2016 at 13:14:08 UTC, Picaud Vincent wrote: > bool opEquals()(auto ref const BigInt y) const pure @nogc > { > return sign == y.sign && y.data == data; > } > > my problem is that I do not understand the role/meaning of "auto" in this context. See https://dlang.org/spec/template.html#auto-ref-parameters. It's used to end up with an `opEquals(ref const BigInt y)` for lvalue args (passed by reference) and with an `opEquals(const BigInt y)` for rvalue args (passed by value => implicitly moved in D (as they are rvalues)). > Moreover in the opCmp code, "auto" is not present anymore, which is an extra source of confusions for me. > > int opCmp(ref const BigInt y) pure nothrow @nogc const > { > // Simply redirect to the "real" opCmp implementation. > return this.opCmp!BigInt(y); > } TypeInfo_Struct apparently requires (or used to require) an `int opCmp(ref const T rhs)` overload, i.e., a version taking the rhs lvalue argument by reference (see https://dlang.org/spec/operatoroverloading.html#compare). Note that there are other overloads afterwards which take the rhs argument by value, thereby allowing rhs rvalues too. |
December 18, 2016 Re: meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Basile B. | On Sunday, 18 December 2016 at 14:25:04 UTC, Basile B. wrote:
> ...
> As you can see, auto ref is more flexible with the parameter. This make sense for templated functions.
Thank you for your detailed answer, things are perfectly clear now. Also sorry for the doc linksI should have found it before asking my question.
|
December 18, 2016 Re: meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Posted in reply to kinke | On Sunday, 18 December 2016 at 14:32:08 UTC, kinke wrote:
> TypeInfo_Struct apparently requires (or used to require) an `int opCmp(ref const T rhs)` overload, i.e., a version taking the rhs lvalue argument by reference (see https://dlang.org/spec/operatoroverloading.html#compare). Note that there are other overloads afterwards which take the rhs argument by value, thereby allowing rhs rvalues too.
Thank you for your complementary answer and explanation. All these look less strange to me now.
|
December 20, 2016 Re: meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Picaud Vincent | As a general rule, 'auto ref' should probably be const. If the purpose of 'ref' is so that the argument would be mutated, then allowing a copy of an rvalue to this function could very well be a bug: struct S { int i; } void foo()(auto ref S s) { s.i = 42; // <-- Cannot be observed if the arg is rvalue } void main() { foo(S(1)); } To contradict myself (and I hate when I do that! :p), the function may be using the rvalue in a non-const context, which would make the mutation observable: struct S { int i; void sayIt() { import std.stdio; writeln(i); } } void foo()(auto ref S s) { s.i = 42; s.sayIt(); // <-- Here } // ... Another one through the return value (but this time it's a copy anyway, perhaps defeating the 'ref' purpose): // ... S foo()(auto ref S s) { s.i = 42; return s; } void main() { foo(S(1)).sayIt(); // <-- Here } Ali |
December 20, 2016 Re: meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Picaud Vincent | On 12/18/2016 05:14 AM, Picaud Vincent wrote: > Another interrogation for me, who come from C++, is how to translate > into D: > > template<typename T> void foo(T&& t); If it means "rvalue reference"[1], then there is no equivalent is D because D does not allow references to rvalues, even if const. If the purpose is optimization, the good news are * Classes are already reference types so there is no lvalue or rvalue reference distinction there * rvalue structs are automatically moved to functions when passed by-copy import std.stdio; struct S { double i; ubyte[1000] buf; this(int i) { this.i = i; writefln("constructor %s", i); } this(this) { writef( "post-blit %s -> ", i); this.i += 0.1; this.buf = buf.dup; writeln(i); } ~this() { writefln("destructor for %s", i); } } void foo(S s) { writefln( "foo(by-copy) %s", s.i); } void foo(ref const(S) s) { writefln( "foo(by-ref-const) %s", s.i); } // UNCOMMENT THIS TO BE SURPRISED: // void foo(ref S s) { // writefln( "foo(by-ref) %s", s.i); // } void main() { { writeln("\n--- rvalue ---"); foo(S(1)); } { writeln("\n--- mutable lvalue ---"); auto s = S(2); foo(s); } { writeln("\n--- const lvalue ---"); const s = S(3); foo(s); } } According to the output, there is no post-blit executed for the rvalue: --- rvalue --- constructor 1 foo(by-copy) 1 destructor for 1 --- mutable lvalue --- constructor 2 post-blit 2 -> 2.1 foo(by-copy) 2.1 destructor for 2.1 destructor for 2 --- const lvalue --- constructor 3 foo(by-ref-const) 3 destructor for 3 There is a surprising difference in D: * First, in C++, you cannot have both the by-copy and by-ref-to-const overload of a function: It would be ambiguous for rvalues. * You can have that in D, which brings the interesting difference: In D, non-constness of an object seems to be more important in overload resolution: Notice how mutable lvalue above is passed to by-copy instead of the potentially-more-optimal by-const-ref above. D realizes that a mutable object is for mutation and because by-const-ref cannot mutate it, D passes it to the by-copy function. (This may be seen as a bug by some.) Interestingly, enabling the by-mutable-ref overload above, now the mutable object goes to by-ref and there is no automatic copy: --- rvalue --- constructor 1 foo(by-copy) 1 destructor for 1 --- mutable lvalue --- constructor 2 foo(by-ref) 2 destructor for 2 --- const lvalue --- constructor 3 foo(by-ref-const) 3 destructor for 3 Ali [1] I have an issue with "rvalue reference" as rvalue references can be references to lvalues as well. :p |
December 20, 2016 Re: meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Tuesday, 20 December 2016 at 19:24:32 UTC, Ali Çehreli wrote:
> As a general rule, 'auto ref' should probably be const. If the purpose of 'ref' is so that the argument would be mutated, then allowing a copy of an rvalue to this function could very well be a bug:
>
> struct S {
> int i;
> }
>
> void foo()(auto ref S s) {
> s.i = 42; // <-- Cannot be observed if the arg is rvalue
> }
>
> void main() {
> foo(S(1));
> }
Thank you Ali! This is effectively a trap I had not realized, you probably save me from some long debugging time.
|
December 20, 2016 Re: meaning of "auto ref const"? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Tuesday, 20 December 2016 at 20:08:32 UTC, Ali Çehreli wrote: > If the purpose is optimization, the good news are Yes it is :) > * Classes are already reference types so there is no lvalue or rvalue reference distinction there Ok, this one is quite intuitive. > import std.stdio; > ... Thank you for the illustrative example, I have reproduced it. > There is a surprising difference in D: > > In D, non-constness of an object seems to be more important in overload resolution: Notice how mutable lvalue above is passed to by-copy instead of the potentially-more-optimal by-const-ref above. D realizes that a mutable object is for mutation and because by-const-ref cannot mutate it, D passes it to the by-copy function. (This may be seen as a bug by some.) Thank you for pointing out this. I was not aware of that, and for sure this is not the C++ behavior. > Interestingly, enabling the by-mutable-ref overload above, now the mutable object goes to by-ref and there is no automatic copy: Ok, that is "moral" and without surprise. > --- rvalue --- > constructor 1 > foo(by-copy) 1 > destructor for 1 > > --- mutable lvalue --- > constructor 2 > foo(by-ref) 2 > destructor for 2 > > --- const lvalue --- > constructor 3 > foo(by-ref-const) 3 > destructor for 3 > > Ali > > [1] I have an issue with "rvalue reference" as rvalue references can be references to lvalues as well. :p Thank you for your time and these valuable explanations, I learnt a lot. --Vincent |
Copyright © 1999-2021 by the D Language Foundation