Does D really need something like const&?

Steven Schveighoffer schveiguy at yahoo.com
Fri Mar 1 17:15:20 PST 2013


On Fri, 01 Mar 2013 19:49:54 -0500, Era Scarecrow <rtcvb32 at yahoo.com>  
wrote:

> On Friday, 1 March 2013 at 23:46:36 UTC, Steven Schveighoffer wrote:

>> This is the major problem that Andrei had with it (at least as I  
>> understand his past statements) -- it conflates const with rvalue  
>> references.  Sometimes, you want a const ref that does NOT bind to an  
>> rvalue.
>
>   If I have a medium sized struct that I only intend to read/reference  
> from I see no reason not to use 'const ref' for simplicity and speed  
> (and avoid postblit hopefully) but when it's an Rvalue I need to make a  
> second function either duplicate except signature or forwarding the  
> value.

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 larg struct, and I want it to be  
const, for contract purposes, I DIDN'T want it to accept rvalues.

The point is, there is a legitimate reason to mark a parameter const ref  
BESIDES wanting to have it bind to rvalues.

>> The one huge problem I've had with lack of rvalue references is with  
>> arithmetic operators:
>>
>> struct M
>> {
>>   M opAdd(const ref M other) const {...}
>> }
>>
>> M m;
>>
>> auto m2 = (m + m) + m; // ok!
>> auto m3 = m + (m + m); // error!
>>
>> This is crap.
>
>   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.

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".

-Steve


More information about the Digitalmars-d mailing list