Why can't structs be derived from?

Jonathan M Davis jmdavisProg at gmx.com
Tue Mar 15 08:00:54 PDT 2011


On Tuesday 15 March 2011 06:25:13 Jens wrote:
> It seems rather fundamental to be able to compose a new struct from a
> given struct using inheritance. Why is this not allowed?
> 
> struct slist_node
> {
>     slist_node* next;
> };
> 
> template <class T>
> struct slist_node<T>: public slist_node
> {
>     T data;
> };

Classes are polymorphic. Structs are not. This is by design. One of the major 
reasons for this is slicing. In C++, if you have a pointer to an object, the 
pointer can be of a base type or of the same type as what is pointed to. You 
don't necessarily care. The correct code is called polymorphically. When you 
assign it to another pointer, it works just fine. You can even safely assign it 
to a pointer of a base type.

class A {}
class B : public A {}

A* a = new B();
B* b = new B();

You can do the same in D with classes and references (and pointers, except that 
pointers aren't polymorphic in D). However, what happens if the type is on the 
stack directly?

A a = *(new B());
B b = *(new B());

Your object just got sliced ( http://en.wikipedia.org/wiki/Object_slicing ). 
When it's on the stack, the compiler must know _exactly_ what type of object it 
is. That means that you can't have it be a different type than what you say it 
is. And that means that while assigning a B to a B works just fine, assigning a B 
to an A means that all the parts of that object that are B-specific, get lost. 
Only the parts that are part of an A get copied (since you just assigned it to 
an A). Take

class A
{
    int x;
}

class B : public A
{
    int y;
    float z;
}

An A has an int in it. A B has an additional y and z in it. When you assign a B 
to an A, the y and z variables get lost. In many cases, this results in a 
completely invalid object and is _rarely_ what you really want. If you're not 
careful, this can be a big problem in C++. In C++, you _must_ use pointers if 
you want polymorphism. This also means that you generally avoid assigning 
dereferenced pointers to stack variables.

D's solution is to make it so that there is a clear separation between non-
polymorphic types on the stack and polymorphic types on the heap. Structs have 
no polymorphism and no inheritance. They live on th stack. Classes do have 
polymorphism nad ihneritance. They live on the heap. You _can_ have pointers to 
structs, but they're still non-polymorphic, and you can't normally stick a class 
on the stack.

There are other benefits to making structs non-polymorphic (such as not needing a 
virtual table), but the main one is to avoid the slicing issue. Overall, it 
works quite well. One of the major, potential sources of bugs in C++ is 
completely avoided in D.

- Jonathan M Davis


More information about the Digitalmars-d mailing list