Rainer Deyke wrote:
- Implementation inheritance of value types, even without actual
polymorphism, is useful. See, for example, the curiously recurring
template pattern (and all of its uses).
You can do this with 'alias this'.
And, yes, you can achieve similar results in D by using template
mixins, but this is a specialized mechanism where C++ manages to do with
the same generalized mechanism used for polymorphism and interface
inheritance.
It should be a different mechanism to show that it is a very different
thing. Value types aren't polymorphic and shouldn't look like they are.
I use inheritance of value types all the time.
- Polymorphic references to value types are often useful. D has
references to value types (in the form of 'ref' parameters and pointers)
but these are not polymorphic. As an example, I would name the standard
C++ iostream.
iostream isn't a good example of design <g>.
They're value types (non-copyable for the most part, but stored
directly instead of as pointers, with RAII), but they're often passed
around as polymorphic references.
I.e. they're a mess. You just confused the heck out of me.
I use polymorphic references to value types occasionally.
- C++ makes a clear distinction between a reference and the thing
being referenced. Even if value types and polymorphism were mutually
exclusive, this distinction would be useful. All types are consistently
treated as value types, even those types that reference another object.
I *like* having to write 'gc_ptr<Object> p;' instead of 'Object p;'.
I *like* having to write 'p->f();' instead of 'p.f();'. It keeps my
code clearly and more explicit.
- C++ does not have separate rules for value types and reference
types. All types are implicitly value types; values of all types can be
placed on the heap. This simplifies the language by having just one set
of rules instead of separate rules for classes and structs. Again, this
unification would be useful even if it was an error to declare a
variable of a polymorphic type as a direct variable.
That philosophy conflicts with using . for values and -> for references.
deterministic destructors, arbitrary copy constructors, and optional
lack of default constructor.
Struct have that except for default constructor. In D, currently default
constructors cannot execute code. This is a limitation that we might
need to address, although it has some advantages.
There are things that copy constructors can do that the post-blit
operator can't do. Also, unless I am mistaken, D can move value types
around in memory at will, which also invalidates designs that are useful
in C++.
The capability of being able to move value types is deliberately
designed in. It's there for the obvious reason of being able to create a
moving garbage collector, and for the more subtle reason of being able
to optimize away many more cases of copy-construction and destruction.