this() not executing code on structs
Denis Koroskin
2korden at gmail.com
Wed Oct 21 12:47:46 PDT 2009
On Wed, 21 Oct 2009 20:15:16 +0400, Andrei Alexandrescu
<SeeWebsiteForEmail at erdani.org> wrote:
> Today, structs can't write their own this(). There aren't very solid
> reasons for that except that it makes language implementation more
> difficult.
>
> I wonder how much of a problem that could be in practice. I realized
> today that the "Counted" example - a classic C++ primer example
> featuring a struct that counts its own instances - cannot be implemented
> in D.
>
> In C++ the counted example looks like this:
>
> struct Counted {
> static unsigned count;
> unsigned myCount;
> Counted() { myCount = count++; }
> Counted(const Counted& rhs) { myCount = count++; }
> Counted& operator=(const Counted& rhs) {
> // no writing to myCount
> return *this;
> }
> ~Counted() {
> --count;
> }
> }
>
> In D there's no chance to write Counted because you can always create
> Counted objects without executing any code.
>
> struct Counted {
> static uint count;
> uint myCount;
> this() { myCount = count++; } // ERROR
> this(this) { myCount = count++; }
> ref Counted opAssign(Counted rhs) {
> // no writing to myCount
> return this;
> }
> ~this() {
> --count;
> }
> }
>
> This being a toy example, I wonder whether there are much more serious
> examples that would be impossible to implement within D.
>
>
> Andrei
I agree it's a very annoying limitation, but I believe there is a
rationale behind it.
Imagine a class that aggregates a struct:
struct Foo { ... }
class Bar
{
Foo foo;
this() {}
// ...
}
The class object construction is now consists of two steps:
1) memcpy the Bar.classinfo.init
2) call __ctor()
Unlike C++, there is no stage at which class members are being
initialized, and it simplifies things quite a lot.
Think about the following: what happens if structs will be allowed default
ctors? How to avoid double initialization?
You may end up with design very similar to C++ in this case (including an
initialization list).
Some problems could be solved with an enforced explicit initialization of
each member. That's the only way I see now that would avoid double
initialization of a struct in presence of default and non-default ctors:
struct Scoped(T) // I like this name a lot more than InPlace, and
especially InSitu
{
this(Args...)(Args args)
{
T obj = cast(T)data.ptr;
obj.__ctor(args);
}
ubyte[T.classinfo.init.length] data = T.classinfo.init; // I believe
this should work at compile-time, since classinfo is immutable
}
class Foo
{
this() {}
this(int i) {}
}
class Bar
{
Scoped!(Foo) foo;
this()
{
// foo is already constructed by now (default ctor is called)
which is cool
// but what if I need to initialize it with some other ctor?
foo = Scoped!(Foo)(42); // double initialization
}
int doesntNeedToBeInitializedExplicitlyInACtor = 17;
}
Enforcing explicit initialization of each member (unless its value is set
at the declaration, as in the example above) is gracefully solving this
issue.
Also think about exception safety: what if an exception is thrown in a
class ctor - should the dtors be invoked on initialized members or not? If
yes, in what order? D doesn't enforce initialization order, so it's a bit
tricky to determine what members are already initialized and need to be
destroyed (with a dtor call) efficiently. C++ handles issues like this
very well IMO. I believe D should also have a sound solution to this
problem.
More information about the Digitalmars-d
mailing list