On Monday, 16 May 2022 at 15:18:11 UTC, Kevin Bailey wrote:
I would say that assignment isn't any more or less of an issue,
as long as you pass by reference:
What I mean is if you write your code for the superclass, and
later add a subclass with some invariants that depends on the
superclass fields… then upcast an instance of the subclass to the
superclass and pass it on… your risk the same issue. The subclass
invariant can be broken because of sloppy modelling.
The premise for "slicing" being an issue is that people who write
functions have no clue about how the type system works or how to
properly model. So it is a lot of fuzz over nothing, because with
that assumption you can make the exact same argument for
references. (I've never had any practical issues related to
"slicing", ever…)
Besides, slicing can very well be exactly what you want, e.g. if
you have a super-class "EntityID" and want to build an array of
all the EntityIDs… nothing wrong about slicing out the EntityID
for all subclass instances when building that array.
Now, there are many other issues with C++, mostly related to the
fact that they give very high priority to avoid overhead. E.g.
take a new feature like std::span, if you create a subspan
("slice" in D terminology) and the original span does not contain
enough elements then C++ regards that as undefined behaviour and
will happily return a span into arbitrary memory past the end of
the original span. C++ is very unforgiving in comparison to
"higher level" languages like D.
If we extend this reasoning to D classes, one can say that D
classes are convenience constructs that does not pay as much
attention to overhead. One example of this is how interfaces are
implemented, each interface will take a full pointer in every
instance of the class. The monitor mutex is another example. And
how pointers to classes are different (simpler syntax) than
pointers to struct also suggests that classes are designed more
for convenience than principles.
Whether this is good or bad probably depends on the user group:
1. Those that are primarily interested in low level with a bit of
high level might think it is "too much" and favour structs.
2. Those that are primarily interested in high level with a bit
of low level might think otherwise.
In C++ everyone belong to group 1. In other system languages such
as D and Rust you probably have a large silent majority in group
2. (All those programmers that find C++ to be too brittle or hard
to get into, but want comparable performance.)