View mode: basic / threaded / horizontal-split · Log in · Help
November 07, 2012
Re: Const ref and rvalues again...
On 11/07/2012 04:01 PM, martin wrote:
> On Wednesday, 7 November 2012 at 10:33:03 UTC, Timon Gehr wrote:
>> You are still missing that const in C++ is different from const in D.
>> const in C++ does not mean anything. It is just loosely enforced
>> interface documentation. const in D actually restricts what the callee
>> can do with the argument, in a transitive fashion.
>
> I still don't get the big difference (except for transitiveness for
> pointers).

That is the main thing, but C++ also has a 'mutable' keyword.

> Given a const reference, I'm only able to invoke methods
> decorated with the const keyword (or inout in D) keyword, both in C++
> and D. And I can only pass it as argument by ref to functions which do
> not alter it (also taking a const reference, that is).
>

This is not the case in C++ for the D definition of 'not alter'.

>> Also, if the point is to have higher speed, why shouldn't the function
>> be allowed to use an rvalue as scratch space without a _deep copy_ ?
>
> I'm sorry but I don't get what you mean here. Could you please elaborate
> on this?
>

Well, if the changes are not visible, the function can freely change the 
memory in order to perform its computations.

>> When my struct does not support any operations that are const, and
>> creating a mutable copy is not possible due to indirections?
>
> If your struct doesn't support any const operations, it most likely has
> good reasons not to.
>

Either that, or someone hasn't bothered to annotate.

>> The change may well be visible...
>
> True in this case, but only if you know exactly what foo() does when you
> call it inside the main() function. And that is probably an indicator
> for bad encapsulation

The method is called 'foo', that means it is a toy example to show that 
the change may be visible. The change does not necessarily have to be 
visible to the caller directly.

> - most of the time, you shouldn't have the
> knowledge how foo() is exactly implemented when using it from the outside.

You got it exactly reverse. const harms encapsulation because it exposes 
some details about implementations. It is often not applicable in 
sufficiently dynamic code.


> It is clear that there are some examples where you want to pass an
> rvalue argument to a mutable ref parameter if you know exactly what the
> function does. But imo these cases are very rare and I don't really
> regard it as big issue if you need to add a line 'auto tmp = myRvalue;'
> before the function call to transform it to a referenceable lvalue, in
> these few cases.

You do not see issues with changing the interface based on 
implementation details?

> Much more commonly, you need a parameter just as read-only input.
> Consider a real-word-example of a 4x4 matrix consisting of 16 doubles
> (128 bytes). Most of the time, you'd only need a read-only input
> instance when working with it (combining matrices, transforming vectors
> etc.). Given its size (it's not really huge, I acknowledge that ;)), you
> probably want to avoid copying it around and therefore pass it by ref,
> but want that to also work for rvalues (produced by matrix combinations
> like 'viewMatrix * modelMatrix', for example).
>
> struct Matrix
> {
>      double[16] data;
>
>      // this op= other
>      ref Matrix opOpAssign(string op)(in ref Matrix other);
>
>      // Matrix result = this op other
>      Matrix opBinary(string op)(in ref Matrix other) const;
>
>      // double4 result = this * vector
>      // the vector (32 bytes) may be passed by value for AVX
>      double4 opBinary(string op)(in ref double4 vector) const
>          if (op == "*");
> };

I know your use case, and note that I do not have any issues with 
rvalues always being allowed to bind to ref parameters, which would 
solve your problem in all cases. (others do not like that though, which 
is why we have the current situation and your C++ - inspired design is 
discussed every second week or so.)
November 07, 2012
Re: Const ref and rvalues again...
On Wednesday, 7 November 2012 at 14:07:31 UTC, martin wrote:
> C++:
> void f(T& a) { // for lvalues
>     this->resource = a.resource;
>     a.resetResource();
> }
> void f(T&& a) { // for rvalues (moved)
>     this->resource = a.resource;
>     a.resetResource();
> }
>
> D:
> void f(ref T a) { // for lvalues
>     this.resource = a.resource;
>     a.resetResource();
> }
> void f(T a) { // rvalue argument is not copied, but moved
>     this.resource = a.resource;
>     a.resetResource();
> }

You could probably get away with a single-line overload, both in 
C++ and D:

C++:
void f(T& a) { // for lvalues
    // convert a to mutable rvalue reference and
    // invoke the main overload f(T&&)
    f(std::move(a));
}

D:
void f(T a) { // rvalue argument is not copied, but moved
    // the original argument is now named a (an lvalue)
    // invoke the main overload f(ref T)
    f(a);
}
November 07, 2012
Re: Const ref and rvalues again...
On Wednesday, 7 November 2012 at 16:57:47 UTC, Timon Gehr wrote:
> That is the main thing, but C++ also has a 'mutable' keyword.

Right, I forgot this inconspicuous little keyword. It really is a 
huge hole in C++'s const system.

>> If your struct doesn't support any const operations, it most 
>> likely has good reasons not to.
>
> Either that, or someone hasn't bothered to annotate.

Hehe, yeah, the latter being more likely.

> You got it exactly reverse. const harms encapsulation because 
> it exposes some details about implementations. It is often not 
> applicable in sufficiently dynamic code.

I don't see it that way. I find it very useful to know that an 
argument won't be modified. Example: suppose we have a large 
array (static array or wrapped in a struct) and need it as input 
for some independent operations. By knowing it won't be touched 
(passing by const ref) it is immediately clear that the 
operations can be run in parallel. Otherwise, one would need to 
go through the operations' implementation to determine if 
parallelization is possible.

> You do not see issues with changing the interface based on 
> implementation details?

I find it okay to require all possible changes to an argument 
passed by reference to be required to be directly visible by the 
caller, just to prevent accidentally missing side effects. 
Suppose a function takes a const reference parameter and is 
called at a site using an rvalue. After some time, that function 
is updated so that its parameter is or may be changed, changing 
the const ref parameter to a ref parameter. That may lead to big 
issues for the call sites. A compiler error would make sure the 
affected call sites are inspected and updated instead of 
potentially introducing regressions.
November 07, 2012
Re: Const ref and rvalues again...
On 11/07/2012 06:52 PM, martin wrote:
> On Wednesday, 7 November 2012 at 16:57:47 UTC, Timon Gehr wrote:
>> That is the main thing, but C++ also has a 'mutable' keyword.
>
> Right, I forgot this inconspicuous little keyword. It really is a huge
> hole in C++'s const system.
>
>>> If your struct doesn't support any const operations, it most likely
>>> has good reasons not to.
>>
>> Either that, or someone hasn't bothered to annotate.
>
> Hehe, yeah, the latter being more likely.
>
>> You got it exactly reverse. const harms encapsulation because it
>> exposes some details about implementations. It is often not applicable
>> in sufficiently dynamic code.
>
> I don't see it that way.

You do not give a justification.

> I find it very useful to know that an argument
> won't be modified.

That is unrelated and does not necessarily follow, but that it does in 
some cases is the main reason why const is useful even though it weakens 
encapsulation.

> Example: suppose we have a large array (static array
> or wrapped in a struct) and need it as input for some independent
> operations. By knowing it won't be touched (passing by const ref) it is
> immediately clear that the operations can be run in parallel. Otherwise,
> one would need to go through the operations' implementation to determine
> if parallelization is possible.
>

You have to do that anyway if it the operation is not at least 'const 
pure' and you need to guarantee that no other thread is in fact 
modifying the data. But even then I do not see how that helps 
encapsulation. Naive parallelization is just one of the examples that 
shows that encapsulation can be harmful in some specific cases.


>> You do not see issues with changing the interface based on
>> implementation details?
>
> I find it okay to require all possible changes to an argument passed by
> reference to be required to be directly visible by the caller, just to
> prevent accidentally missing side effects. Suppose a function takes a
> const reference parameter and is called at a site using an rvalue. After
> some time, that function is updated so that its parameter is or may be
> changed, changing the const ref parameter to a ref parameter. That may
> lead to big issues for the call sites. A compiler error would make sure
> the affected call sites are inspected and updated instead of potentially
> introducing regressions.

Often there is nothing to be seen for the caller. (lazy initialization, 
caching, internal state update part of a larger computation, ...)
November 07, 2012
Re: Const ref and rvalues again...
On Wednesday, 7 November 2012 at 18:07:27 UTC, Timon Gehr wrote:
> You do not give a justification.
>
>> I find it very useful to know that an argument
>> won't be modified.
>
> That is unrelated and does not necessarily follow, but that it 
> does in some cases is the main reason why const is useful even 
> though it weakens encapsulation.

Let's look at encapsulation this way: suppose my function is a 
colleague and his job is to summarize a book I lend him (my 
reference parameter). I don't care how he does it as long as the 
summary (function result) is what I expect. But I do care about 
my book if I plan on using it afterwards (so in case the argument 
is an lvalue used later on) - will I have to check it for 
defects, coffee spillings etc., or can I be sure it's exactly as 
it was before (const)? This is how I see it.
The thing about mutable rvalue references would be unknown or 
unthought-of side effects: what if the book is the same, but my 
colleague, a known psychopath, killed the author (a property of 
the book reference, a reference itself)? I don't use the book 
anymore, okay (it was an rvalue after all), but it may still be 
worth knowing that the author has gone. Due to the transitiveness 
of const, the (const) author would still be alive if the book 
reference had been const.

> You have to do that anyway if it the operation is not at least 
> 'const pure' and you need to guarantee that no other thread is 
> in fact modifying the data.

I think we can agree that knowing the parameters aren't touched 
does help a lot in this case.
November 07, 2012
Re: Const ref and rvalues again...
On Wednesday, 7 November 2012 at 14:07:31 UTC, martin wrote:

> T g() {
>     T temp;
>     temp.createResource();
>     return temp; // D already moves temp!
>                  // 'Named Return Value Optimization'
> }

OK, it seems to be working as you describe.

The main question I have is if I can rely on this behaviour as a 
feature provided by the languge specification vs an opportunistic 
compiler optimization that may or may not happen depending on the 
implementation?

The general problem I'm having with with D, is I need a gaurantee 
that certain behaviors will always be performed when moves/copies 
are done, but it's just not clear if this is the case or not. 
There seems to be a lack of a concise language specification, or 
is there one defined somewhere?

> You could implement a copy constructor 'this(this)' in your 
> struct T and see when it is invoked to check when an instance 
> is actually copied. I'd expect that invoking 'f(g());' with 
> above implementation doesn't copy anything.

I am doing that, but I cannpt seem to hook into the part where a 
move does an "init" on the struct or class. I sort-of can see it 
though, if I set a member value to a known state before the move, 
then display it to console or log after the move during 
destruction. If it was init'ed then I'll see the default value, 
and this seems to be happening as described.

Anyway, what I was hoping for with "auto ref" was for the 
compiler to selectively decide if the ref part should be used or 
not depending on the situation, rather than me manually writing 
two functions to do the exact same thing. What's discussed in 
here about auto ref is something else, although I agree it is 
definitely needed.

--rt
November 07, 2012
Re: Const ref and rvalues again...
On 11/07/2012 07:48 PM, martin wrote:
> On Wednesday, 7 November 2012 at 18:07:27 UTC, Timon Gehr wrote:
>> You do not give a justification.
>>
>>> I find it very useful to know that an argument
>>> won't be modified.
>>
>> That is unrelated and does not necessarily follow, but that it does in
>> some cases is the main reason why const is useful even though it
>> weakens encapsulation.
>
> Let's look at encapsulation this way: suppose my function is a colleague
> and his job is to summarize a book I lend him (my reference parameter).
> I don't care how he does it as long as the summary (function result) is
> what I expect. But I do care about my book if I plan on using it
> afterwards (so in case the argument is an lvalue used later on) - will I
> have to check it for defects, coffee spillings etc., or can I be sure
> it's exactly as it was before (const)?

You can pass him an object that does not support operations you want to 
preclude. He does not have to _know_, that your book is not changed when 
he reads it. This is an implementation detail. In fact, you could make 
the book save away his reading schedule without him noticing.

> This is how I see it.
> The thing about mutable rvalue references would be unknown or
> unthought-of side effects: what if the book is the same, but my
> colleague, a known psychopath, killed the author (a property of the book
> reference, a reference itself)? I don't use the book anymore, okay (it
> was an rvalue after all), but it may still be worth knowing that the
> author has gone. Due to the transitiveness of const, the (const) author
> would still be alive if the book reference had been const.
>

I'd assume that the book you pass him would not support changing the author.

>> You have to do that anyway if it the operation is not at least 'const
>> pure' and you need to guarantee that no other thread is in fact
>> modifying the data.
>
> I think we can agree that knowing the parameters aren't touched does
> help a lot in this case.

Maybe, but the fact that we know it harms encapsulation.
November 07, 2012
Re: Const ref and rvalues again...
On 7 November 2012 04:22, martin <kinke@libero.it> wrote:

> On Wednesday, 7 November 2012 at 01:33:49 UTC, Jonathan M Davis wrote:
>
>> The most recent discussion where Walter and Andrei were part of the
>> discussion
>> was here:
>>
>> http://forum.dlang.org/post/**4F84D6DD.5090405@digitalmars.**com<http://forum.dlang.org/post/4F84D6DD.5090405@digitalmars.com>
>>
>
> That thread is quite misleading and, I'm sad to say, not very useful
> (rather damaging to this discussion) in my opinion - especially because the
> distinction between rvalue => 'const ref' and rvalue => ref is largely
> neglected, and that distinction is of extremely high importance, I can't
> stress that enough. Walter's 3 C++ examples (2 of them invalid anyway
> afaik) don't relate to _const_ references. The implicit type conversion
> problem in that thread isn't a problem for _const_ references, just to
> point out one tiny aspect.
> rvalue => ref/out propagation makes no sense imho, as does treating
> literals as lvalues (proposed by Walter iirc). The current 'auto ref'
> semantics also fail to cover the special role of _const_ references for
> rvalues (also illustrated by Scarecrow's post).
>
>
>  Certainly, it's not a simple matter of just making const
>> ref work with rvalues like most of the people coming from
>> C++ want and expect.
>>
>
> Well I absolutely do _not_ share this point of view. It just seems so
> logical to me. I'm still waiting for a plausible argument to prove me
> wrong. All the required info is in this thread, e.g., we covered the
> escaping issue you mentioned.
>

+1
I couldn't possibly agree more with your entire post.
November 08, 2012
Re: Const ref and rvalues again...
On Wednesday, 7 November 2012 at 21:39:52 UTC, Timon Gehr wrote:
> You can pass him an object that does not support operations you 
> want to preclude. He does not have to _know_, that your book is 
> not changed when he reads it. This is an implementation detail. 
> In fact, you could make the book save away his reading schedule 
> without him noticing.

I don't see where you want to go with this. Do you suggest 
creating tailored objects (book variants) for each function 
you're gonna pass it to just to satisfy perfect theoretical 
encapsulation? So foo() shouldn't be able to change the author => 
change from inout author reference to const reference? bar() 
should only be allowed to read the book title, not the actual 
book contents => hide that string? ;) For the sake of simplicity, 
by using const we have the ability to at least control if the 
object can be modified or not. So although my colleague doesn't 
have to _know_ that he can't modify my book in any way (or know 
that the book is modifiable in the first place), using const is a 
primitive but practical way for me to prevent him from doing so.

In the context of this rvalue => (const) ref discussion, const is 
useful due to a number of reasons.

1) All possible side effects of the function invokation are 
required to be directly visible by the caller. Some people may 
find that annoying, but I find it useful, and there's a 
single-line workaround (lvalue declaration) for the (in my 
opinion very rare) cases where a potential side-effect is either 
known not to occur or simply uninteresting (requiring exact 
knowledge about the function implementation, always, i.e., during 
the whole life-time of that code!).

2) Say we pass a literal string (rvalue) to a const ref 
parameter. The location of the string in memory can then be 
freely chosen by the compiler, possibly in a static data segment 
of the binary (literal optimization - only one location for 
multiple occurrences). If the parameter was a mutable ref, the 
compiler should probably allocate a copy on the stack before 
calling the function, otherwise the literal may not be the same 
when accessed later on, potentially causing funny bugs.

3) Implicit type conversion isn't a problem. Say we pass an int 
rvalue to a mutable double ref parameter. The parameter will then 
be a reference to another rvalue (the int cast to a double) and 
altering it (the hidden double rvalue) may not really be what the 
coder intended. Afaik D doesn't support implicit casting for 
user-defined types, so that may not be a problem (for now at 
least).
November 08, 2012
Re: Const ref and rvalues again...
On Thursday, October 18, 2012 05:07:52 Malte Skarupke wrote:
> What do you think?

Okay. Here are more links to Andrei discussing the problem:

http://forum.dlang.org/post/4F83DBE5.20800@erdani.com
http://www.mail-archive.com/digitalmars-d@puremagic.com/msg44070.html
http://www.mail-archive.com/digitalmars-d@puremagic.com/msg43769.html
http://forum.dlang.org/post/hg62rq$2c2n$1@digitalmars.com

He is completely convinced that letting rvalues bind to const& in C++ was a 
huge mistake, and it seems to come down mainly to this:

"The problem with binding rvalues to const ref is that once that is in place 
you have no way to distinguish an rvalue from a const ref on the callee site."

And apparenly it required adding the concept of rvalue references to C++, 
which complicated things considerably.

It's too bad that he hasn't replied to some of the more detailed questions on 
the matter in this thread, but if you want rvalues to bind to const ref in D, 
you're going to have to convince him.

At this point, I expect that the most likely solution is that auto ref will 
continue to work like it does in templates but for non-templated functions it 
will become like const ref is in C++ and accept rvalues without copying 
lvalues. Andrei suggests that in at least one of those posts, and AFAIK, it 
would work just fine. The only real downside that I'm aware of is that then the 
semantics of auto ref are slightly different for non-templated functions, but 
you're doing basically the same thing with auto ref in both cases - avoiding 
copying lvalues and only copying rvalues when you have to - so it really 
shouldn't be a problem. The main problem is that someone needs to go and 
implement it.

- Jonathan M Davis
3 4 5 6 7 8 9 10 11
Top | Discussion index | About this forum | D home