pointers, functions, and uniform call syntax

monarch_dodra monarchdodra at gmail.com
Mon Sep 3 12:49:37 PDT 2012


On Monday, 3 September 2012 at 18:45:42 UTC, Jonathan M Davis 
wrote:
> On Monday, September 03, 2012 14:13:10 monarch_dodra wrote:
>> I was playing around with a very big struct, and told myself I
>> wanted it allocated on the heap. This meant I was now
>> manipulating S* instead of an S.
>> 
>> I thought "this should have zero impact, because in D, because
>> "." is supposed to deference for you if needed."
>> 
>> I find it strange though that when trying to call a function 
>> with
>> a pointer, the pointer isn't automatically dereferenced if 
>> needed:
>> 
>> ----
>> struct S
>> {
>>    void foo();
>> }
>> void bar(S);
>> void main()
>> {
>>    auto r = new S;
>>    r.foo();
>>    bar(r);  //derp
>>    r.bar(); //derp
>> };
>> ----
>> I find it strange, because I thought the entire point was to
>> abstract way something was allocated to the way it was used: 
>> EG.
>>  From a caller perspective, I don't care if r is on the stack 
>> or
>> on the heap: I just want to call the method bar on the object 
>> in
>> question.
>> 
>> Why does one consider a "free standing" function more ambiguous
>> than a member function?
>> 
>> Things get even stranger if you mix in uniform call syntax.
>> "r.foo()" works, but "r.bar()" doesn't?
>> 
>> Am I really forced into:
>> ----
>> struct S
>> {
>>    void foo();
>> }
>> void bar(S);
>> void main()
>> {
>>    auto r = new S;
>>    r.foo();
>>    bar(*r);    //Groan
>>    (*r).bar(); //Super Groan.
>> };
>> ----
>> I feel as if I'm just back at square 1...
>
> All that UFCS does is make it so that if the first parameter of 
> a function is a
> given type, you can call that function on that type as if it 
> were a member
> function. It's purely syntactic convenience.
>
> void bar(S) {}
>
> takes an S, not as S*, so I wouldn't expect UFCS to work with 
> it and an S*.
>
> . dereferences a pointer when accessing a member function or 
> variable, because
> it works quite nicely to have it work that way and generally 
> negates the need
> for a second operator (->). It's certainly _not_ true that the 
> automatic
> dereferencing with . allows you to forget that something is a 
> pointer. An
> operation which _could_ be on the pointer (e.g ==) will operate 
> on the
> pointer, forcing you to dereference it. It's just that adding 
> -> on top of .
> is unnecessary and complicates the language.
>
> The choice to have . automatically dereference pointers when 
> access members
> predates the idea of UFCS considerably, and I don't think that 
> it was ever
> really considered how they two would interact.  The automatic 
> dereferencing of
> a pointer doesn't really have anything to do with UFCS except 
> for similarites
> of syntax. It's simply that if a variable is a pointer, and it 
> points to a
> type which has a member with the same name as what's on the 
> right-hand side of
> the dot, then that member function gets called. And 
> technically, it doesn't
> even dereference the pointer, because the member function takes 
> a pointer (as
> the invisible this pointer). If there is no such member 
> function, then free
> functions are checked to see if they take the variable's type 
> as their first
> argument. If there's such a function with the right name and 
> number of
> arguments, then it's used. For there to be any dereferencing 
> involved would
> require special casing pointers, which doesn't currently happen.
>
> I think that the way that it currently works is completely 
> consistent. It's a
> perfectly valid enhancement request to want
>
> void func(S s, int i) {...}
>
> to be be callable with S*, given that normally function calls 
> on an S* don't
> require you to dereference anything. But it's not like what we 
> have is broken.
> It's just that there's a corner case which forces you to deal 
> with pointers
> specially (which isn't exactly new, because things like == and 
> assignment
> already require you to treat pointer specially). So, feel free 
> to create an
> enhancement request. You've found a corner case that I suspect 
> was never fully
> though through, and Walter may very well think that the change 
> is worth
> making.
>
> However, one thing to remember that complicates this a bit is 
> that it's
> perfectly possible to declare a function which is overloaded 
> with one function
> taking a pointer and one not.
>
> void func(S* s, int i) {...}
> void func(S s, int i) {...}
>
> in which case, there's an ambiguity, and I would then expect 
> UFCS to _not_
> compile when using S*, or you'd risk function call hijacking. 
> That's not
> necessarily a big deal, but it _does_ complicate things a bit.
>
> - Jonathan M Davis

TY for the reply, I'll consider asking for it.

In the mean time, is there a way to access that variable with 
value semantics? I mean, in the scope where my pointer was 
declared, I *know* it is non null.

In C++, I often did it with iterators:
----
for(it ...)
{
     int& val = *it;
     //now, we can access it with value semantics through val.
}
----

?


More information about the Digitalmars-d mailing list