On Tuesday, 16 October 2018 at 02:26:04 UTC, Manu wrote:
I understand your point but I think the current shared (no
implicit conversion) has its uses. It can be quite useful to
have one interface for when an object is shared and one for
when it is not (one with and without the synchronization
cost). Sure, something as trivial as a counter can be
re-implemented in both ways but more complex objects would
easily result in extreme code duplication.
If you can give a single 'use', I'm all ears ;)
My usages are a custom ref counted template and list types (which
are built on top of the former). The ref counted template will
initialize naively when not shared but utilize compare-and-set
and thread yielding if needed when shared (ensureInitialized can
occur any time after declaration). The list types (which are not
shared-compatible *yet*) will utilize a read-write mutex only
when shared (no cost beyond a little unused memory to non-shared
objects). As such, casting any of the non-shared versions of
these types to shared would be unsafe.
(Technically the types can be safely casted to shared so long as
no non-shared
reference to them exists and vice versa.)
It's also easy to acknowledge that implicit conversion to
shared has its uses.
I actually know of many real uses for this case.
(There was a time when I wanted it as well. I've forgotten what
for by now.)
That might be fine.
Like I said in OP, the first point that I think needs to be
agreed on,
is that shared can not read or write members. I think that's a
pre-requisite for any interesting development.
I will agree that *outsiders* should not be able to read/write
members of a shared object. I'm not sure whether your design
means even within a shared function such members cannot be read
from or written to. If it does, the casting required may get a
little annoying but isn't unworkable (especially since an
unshared template can do the cast in an @trusted manner).