Why can't we make reference variables?

Tommi tommitissari at hotmail.com
Thu Aug 30 22:24:28 PDT 2012


On Thursday, 30 August 2012 at 17:50:47 UTC, Jonathan M Davis 
wrote:
> On Thursday, August 30, 2012 15:30:15 Tommi wrote:
>> Yes, but to me the ambiguity of that example is in whether or 
>> not
>> implicit deferencing of pointers has precedence over uniform
>> function call syntax. Apparently it does, but it's not that
>> obvious that it would.
>
> It _can't_ work any other way, because there's no way to tell 
> the compiler to
> use the member function specifically. You can use the full 
> import path for the
> free function, so you can tell the compiler to use the free 
> function. There's
> no such syntax for member variables.
>
>> struct MyStruct
>> {
>> int _value = 0;
>> 
>> void increment()
>> {
>> ++_value;
>> }
>> }
>> 
>> void increment(ref MyStruct* ptr)
>> {
>> ++ptr;
>> }
>> 
>> void main()
>> {
>> MyStruct* ptrMyStruct = new MyStruct();
>> 
>> // Are we incrementing the pointer using UFCS or
>> // are we calling the member function in MyStruct?
>> ptrMyStruct.increment();
>> }
>
> For instance, if you do
>
> increment(ptrMyStruct);
>
> or
>
> .increment(ptrMyStruct);
>
> or if increment had a longer import path
>
> path.to.increment(ptrMyStruct);
>
> then you can tell the compiler to use the free function. But 
> how would you do
> that with the member function? You can't. So, there's really no 
> other choice
> but to choose the member function whenever there's a conflict. 
> It also prevents
> function hijacking so that something like var.increment() 
> doesn't suddenly
> start using using a free function instead of the member 
> function when you add
> an import which has an increment free function.
>
> - Jonathan M Davis

But this is not about member function vs. free function. This is 
about implicit pointer dereferencing vs. UFCS. The question is: 
"what does member access operator do, when it operates on a 
pointer?". There are two options, and I think they are both valid 
options the language could have chosen:

// S is a struct:
auto ptr = new S();

// What to do with this?
ptr.fun(); // or ptr.fun;

Option #1:
   Rewrite the expression as (*ptr).fun()

Option #2:
   If .fun(S*) exists: call the free function .fun(ptr)
   Else: rewrite the expression as (*ptr).fun()

...But, actually it seems that the language is a bit broken, 
because it doesn't follow neither one of those two options. What 
it actually does is this:

Option #3:
   If S.init.fun() exists: call the member (*ptr).fun()
   Else if .fun(S*) exists: call the free function .fun(ptr)
   Else give a compile time error

Here's the details:

struct S1
{
     int _value = 42;

     void fun()
     {
         ++_value;
     }
}

void fun(ref S1 s1)
{
     s1._value += 1000;
}

void fun(ref S1* ptr1)
{
     ++ptr1;
}
/////////////////////////////
struct S2
{
     int _value = 42;
}

void fun(ref S2 s2)
{
     ++s2._value;
}

void fun(ref S2* ptr2)
{
     ++ptr2;
}
/////////////////////////////
struct S3
{
     int _value = 42;
}

void fun(ref S3 s3)
{
     ++s3._value;
}


void main()
{
     auto ptr1 = (new S1[2]).ptr;
     ptr1.fun();            // calls (*ptr1).fun()
     writeln(ptr1._value);  // prints 43

     auto arr2 = new S2[2];
     auto ptr2 = arr2.ptr;
     arr2[1]._value = 12345;
     ptr2.fun();            // calls .fun(ptr2)
     writeln(ptr2._value);  // prints 12345

     auto ptr3 = (new S3[2]).ptr;
     ptr3.fun(); // Error: function main.fun (ref S3 s3) is
                 // not callable using argument types (S3*)
}

I think, if we want to have implicit pointer dereferencing (I'm 
not even sure it's a good thing), then option #1 would be the 
best choice.


More information about the Digitalmars-d mailing list