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.