D Ranges
monarch_dodra
monarchdodra at gmail.com
Sun Sep 15 01:39:40 PDT 2013
On Saturday, 14 September 2013 at 06:18:02 UTC, Jonathan M Davis
wrote:
> On Friday, September 13, 2013 23:07:03 H. S. Teoh wrote:
>> OTOH, I find myself switching to classes just to get the
>> reference
>> semantics in other cases, even if I never actually do any
>> inheritance.
>> Trying to do reference semantics with structs, while certainly
>> possible,
>> is just too error-prone IME. Just a few days ago, I
>> encountered what
>> looked like a nasty functionality bug in my program, only to
>> eventually
>> discover that it was caused by a missing 'ref' in a function's
>> struct
>> parameter, so updates to the struct didn't persist as the code
>> assumed
>> it would. I found myself seriously considering using classes
>> instead,
>> just for the default ref semantics.
>
> In general, if you want to have structs with reference
> semantics, it's
> probably better to just give them reference semantics by making
> it so that any
> of their members which are value types are on the heap or by
> putting all of
> the struct's guts on the heap. At some point, it becomes
> debatable as to
> whether that's better than using a class, but it does have less
> overhead.
> There's also RefCounted, which does incur the bookkeeping
> overhead of the
> refcounting, but it does make it so that the memory is freed as
> soon as you
> don't need the object anymore.
>
> - Jonathan M Davis
I have a few issues with ref counted.
First, if you *ever* place one in an array or an AA, then you
will leak. It is NOT designed for simply incorporating reference,
but for deterministic finalization.
Second, it has an elaborate postblit and opAssign. This is not a
big issue in itself, but their sole existence does cause dmd to
generate code that is sub-optimal. It also means it can't take
the "optimal" route in a lot of algorithms (array has to emplace
each element individually, for example).
Finally, a RefCounted will implicitly cast to its payload. I
think this is *horrible*. Before you know it, the Payload will
have "jettisoned" its wrapper, and you'll be operating on your
value-type payload "raw". For example:
void foo(T t);
RefCounted!T myRecCounted;
foo(myRefCounted); //Passes. Oops!
The workaround would be to either require explicit "get" to go
from ref counted to payload (breaking existing code), or to
entirelly wrap the RefCounted as a member inside the struct
(requires boilerplate code).
--------
Overall, if I need a reference semantic struct, I find it much
simpler for it to just hold a GC pointer to a payload.
Though to be honest, as H.S. Teoh, I seriously wonder why I even
bother, when I could just use a final class. With proper "private
constructors + non-member "make" function", you can make your
choice outright transparent to the final user too, meaning you
can "fast prototype" with classes, and later change to structs if
you think it is worth it.
More information about the Digitalmars-d
mailing list