pointers, functions, and uniform call syntax
Carl Sturtivant
sturtivant at gmail.com
Mon Sep 3 12:57:42 PDT 2012
On Monday, 3 September 2012 at 19:49:14 UTC, monarch_dodra wrote:
> 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.
> }
> ----
>
> ?
Good question, I've proceeded in the same way in C++ many times.
I hope there's a way, but I think it likely there is not, except
by passing *it to a function with a reference parameter.
More information about the Digitalmars-d
mailing list