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.

Reply via email to