www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - struct vs. class

reply Martin <m_dot_hinsch rug.nl> writes:
Inspired by the recent discussion about iterator structs/classes I wanted to
ask - what's the design rationale behind having both structs and classes? In
C++ the necessity was given by backwards compatibility but that doesn't apply
to D of course. 
The only principal differences I can see (I'm not a D programmer (yet), so
please correct me if I'm wrong) are a) that structs don't contain run-time meta
information (a pointer to a vtable or however that works) and b) that struct
variables are statically allocated as opposed to class variables which always
seem to be pointers into dynamic memory.
Somehow this looks really unorthogonal to me. Especially b) I find strange, can
somebody explain the reason for this?

cheers
Martin
May 28 2007
next sibling parent Gregor Richards <Richards codu.org> writes:
Martin wrote:
 Inspired by the recent discussion about iterator structs/classes I wanted to
ask - what's the design rationale behind having both structs and classes? In
C++ the necessity was given by backwards compatibility but that doesn't apply
to D of course. 
 The only principal differences I can see (I'm not a D programmer (yet), so
please correct me if I'm wrong) are a) that structs don't contain run-time meta
information (a pointer to a vtable or however that works) and b) that struct
variables are statically allocated as opposed to class variables which always
seem to be pointers into dynamic memory.
 Somehow this looks really unorthogonal to me. Especially b) I find strange,
can somebody explain the reason for this?
 
 cheers
 Martin

Firstly, your assertion about C++ is incorrect. C++ doesn't have both, classes are just structs that are private by default, structs are just classes that are public by default. A C struct like this: struct Foo { int a, b; } is 100% equivalent (and passable as) a C++ class like this: class Bar { public: int a, b; } Structs in D are very light-weight. A struct is just a conglomeration of data. Passing a struct which contains two ints is exactly like passing the two ints, but syntactically less gross. Functions defined within a struct are basically syntactic sugar around functions that are defined taking the struct as the first argument. Classes are properly object oriented: They have hierarchies, methods are virtual, etc. As such, they're (comparably to structs) heavyweight: They have classinfo, are allocated on the heap, method calls require looking in a vtable, etc. Your 'b' statement is incorrect. In both cases, the data is allocated where the struct/class is allocated. Just classes are usually on the heap, structs are generally on the stack. - Gregor Richards
May 28 2007
prev sibling parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Martin wrote:
 Inspired by the recent discussion about iterator structs/classes I wanted
 to ask - what's the design rationale behind having both structs and classes?
 [...]

The most important difference is that structs have value semantics and classes have reference semantics. There are very good reasons to have both, and unifying them would be as wrong as taking away pointers or taking away stack objects. Dave
May 28 2007
parent reply Martin <m_dot_hinsch rug.nl> writes:
David B. Held Wrote:

 Martin wrote:
 Inspired by the recent discussion about iterator structs/classes I wanted
 to ask - what's the design rationale behind having both structs and classes?
 [...]

The most important difference is that structs have value semantics and classes have reference semantics. There are very good reasons to have both, and unifying them would be as wrong as taking away pointers or taking away stack objects.

I totally agree that both are needed, but is it necessary to tie the difference to the difference between structs and classes? It would be nice if I could decide on the spot (i.e. when declaring a variable) in which way to use my type.
May 29 2007
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Martin wrote:
 David B. Held Wrote:
 
 Martin wrote:
 Inspired by the recent discussion about iterator structs/classes I wanted
 to ask - what's the design rationale behind having both structs and classes?
 [...]

classes have reference semantics. There are very good reasons to have both, and unifying them would be as wrong as taking away pointers or taking away stack objects.

I totally agree that both are needed, but is it necessary to tie the difference to the difference between structs and classes? It would be nice if I could decide on the spot (i.e. when declaring a variable) in which way to use my type.

Unfortunately, you can't decide "on the spot" whether to use polymorphism or dynamic dispatch (nor can you make this choice on a per-instance basis in any other language I know of). The best you can do is get reference semantics for your structs by using 'ref'. But you will never get exact value semantics for classes because that could lead to slicing, which is why classes/structs in C++ are dangerous. However, you can simulate polymorphic value types by creating invariant classes. If you want to create both struct and class versions of your type and select them on a per-instance basis, you can create a template, but that doesn't make a whole lot of sense to me, so I'm not sure when or why you would do that. Dave
May 29 2007
parent reply Martin <m_dot_hinsch rug.nl> writes:
David B. Held Wrote:

 Martin wrote:
 David B. Held Wrote:
 
 Martin wrote:
 Inspired by the recent discussion about iterator structs/classes I wanted
 to ask - what's the design rationale behind having both structs and classes?
 [...]

classes have reference semantics. There are very good reasons to have both, and unifying them would be as wrong as taking away pointers or taking away stack objects.

I totally agree that both are needed, but is it necessary to tie the difference to the difference between structs and classes? It would be nice if I could decide on the spot (i.e. when declaring a variable) in which way to use my type.

Unfortunately, you can't decide "on the spot" whether to use polymorphism or dynamic dispatch (nor can you make this choice on a per-instance basis in any other language I know of). The best you can do is get reference semantics for your structs by using 'ref'. But you will never get exact value semantics for classes because that could lead to slicing, which is why classes/structs in C++ are dangerous. However, you can simulate polymorphic value types by creating invariant classes. If you want to create both struct and class versions of your type and select them on a per-instance basis, you can create a template, but that doesn't make a whole lot of sense to me, so I'm not sure when or why you would do that. Dave

But if I understand it correctly the only points where problems can occur are in casts. This means that it should be possible to implement safeguards since for a value the type has to be known at compile time anyways whereas a reference will have RTTI. In both cases it's possible (either for the compiler or the run-time) to determine whether the cast is valid. The only reason why C++ fails miserably in this is that per default it just doesn't care about run-time stuff so every check for validity is up to the (potentially lazy) programmer. In D it would be perfectly possible to make a hybrid reference/value system failsafe. So, you could take a struct value and convert it into a class reference (probably by internally creating an object and filling it with values from the struct) and vice versa. Of course you wouldn't call it struct and class but just values and references of the same type. Note that there would be no reason to abandon the PODness of structs, when making a struct value from a class reference you'd just abandon the meta-info. I think that would simplify matters considerably. I might be totally off here but from what I've read in the docs and on the mailing list it looked to me as if the implementations of structs and classes in the compiler are at least to some degree a parallel effort. In any case, from the user's perspective I find the differences and the resulting restrictions on structs quite unintuitive and arbitrary.
May 29 2007
parent "David B. Held" <dheld codelogicconsulting.com> writes:
Martin wrote:
 [...]
 But if I understand it correctly the only points where problems can occur
 are in casts. This means that it should be possible to implement safeguards
 since for a value the type has to be known at compile time anyways whereas
 a reference will have RTTI.
 [...]

I guess my problem is lack of motivation. I'm just not seeing the use cases you are, so I don't see this part of the design space as lacking. Can you show us where you would like to use this feature and how it is better than the way things are now? Dave
May 29 2007