Great responses, everyone. I'll try to address all of them.
Mike, I know the rules. I was asking, "Why is it this way? Why
was it designed this way? What bad thing happens the other way?"
When I think about most things in D, I can at least think of a
reason, even if I don't agree with it. But not putting class
objects on the stack makes no sense to me (assuming we still pass
by reference.) Reasons below.
Ola, your response is very interesting. I would say that
assignment isn't any more or less of an issue, as long as you
pass by reference:
// Using C syntax to make intent clear.
class Foo { int x; }
class Bar: Foo { int y; }
void func1(Bar* bar) {
bar.y = 4; // boom
}
void func2(Bar* bar) {
Foo* foo = bar;
foo.x = 3; // this is fine
foo = new Foo;
func1(cast(Bar*)(foo)); // uh oh
}
Illuminating comment about the ABI.
Ali, I've never liked the distinction between 'struct' and
'class' in C++ either, but that's no reason to actually make them
different. It's a reason to remove 'class' and save the keyword.
re: pass-by-reference and performance, agreed, this is why we
pass integers in registers. But a struct on the stack is just as
fast to access locally through a pointer register - "[bp+6]" - as
remotely, yes?
Finally, 'scope' is nice but it doesn't solve the segfault issue.
HS Teoh: See above for my responses about assignment and 'scope'.
bauss: "But that's not entirely true as you can allocate a struct
on the heap as well."
forkit: "where they live in memory should be less of the
programmers concern, and more an implementation issue (although
some programmers will of course consider this as well)."
Precisely. You can allocate structs, and you can put class
objects in 'scope' variables. (I'm not sure if this was your
intent, forkit, but) a class object can live on the stack just as
easily as on the heap, as long as you pass by reference. The only
danger is if a called function tries to own a stack allocated
object, but this is a concern for heap allocated objects too.
This is why C++ has moveable types and unique_ptr.
Walter, Thanks for the insightful reply! I'm getting the sense
that the decision was made in order to make the language simpler.
That is, ignoring struct's, D went the Java path: You can have
any color you like, as long as it's grey.
I agree that C++'s organic growth allows programmers to do things
they shouldn't, and requires that they be more careful. But I
would not have gone from there to Java.
I think an interesting middle-ground would be a language that had
"template" types - Copyable, MoveOnly, Interface, Singleton,
FactoryBuilt, etc. I've learned from all the projects that I've
been on that we need all these types. We can either ask
programmers to hand-craft them, or we can provide them. Note that
in C++, we can get pretty close:
class Foo: Moveable<Foo> { ...
And then there's the segfault issue. I think that, if we're going
to ignore a huge problem like that, there has to be very strong
reasons. From this discussion, it doesn't sound like they were
very strong.
Of course, it's done and there's little changing it. A seemingly
harmless fix would be to not require 'new':
Foo foo; // this allocates an object
Foo foo = null; // this does not
Foo foo = function(); // this doesn't either
In fact, I suspect we could make that change today and few would
notice. Nevertheless, I'm still a little shocked that this isn't
existing behavior.
cheers all