Thread overview
Inherited constructor
Jul 25
Jerry
5 days ago
Luna Nielsen
July 24

What is considered the "clean" way to address this error:

tst60.d(7): Error: class `tst60.B` cannot implicitly generate a default constructor when base class `tst60.A` is missing a default constructor

Yes, this is a toy example, but it reflects something I'm doing in actual code. The subclass has no state, thus it's fine to let the superclass take care of initialization. Do I really need a shim like this:

    this(int val) {
        super(val);
    }

In my class B just to placate the compiler? Is this considered the idiomatic way to deal with such a class organization?

class A {
    int v;
    this(int val) {
        this.v = val;
    }
}
class B : A {
    int myfunc() {
        return this.v;
    }
}
int main() {
    auto b = new B(1);
    return b.myfunc();
}
July 25

On Thursday, 24 July 2025 at 17:19:17 UTC, Andy Valencia wrote:

>

What is considered the "clean" way to address this error:

tst60.d(7): Error: class `tst60.B` cannot implicitly generate a default constructor when base class `tst60.A` is missing a default constructor

Yes, this is a toy example, but it reflects something I'm doing in actual code. The subclass has no state, thus it's fine to let the superclass take care of initialization. Do I really need a shim like this:

    this(int val) {
        super(val);
    }

In my class B just to placate the compiler? Is this considered the idiomatic way to deal with such a class organization?

class A {
    int v;
    this(int val) {
        this.v = val;
    }
}
class B : A {
    int myfunc() {
        return this.v;
    }
}
int main() {
    auto b = new B(1);
    return b.myfunc();
}

This is a classic case where A is the "ancestor" class and B is the "heir" class.
If you don't recognize Eiffel language, let's try it in C# or D.

A is the "base" or "parent" class and B is the "derived" or "child" class.
Constructors are not inherited. Each class must provide its own.

When "parent" class has a constructor, derived classes must also have a constructor.
You are welcome to create a do-nothing "child" constructor.

D requires you to "provide the shim" as you indicate.

D requires that a derived class constructor call super(val) somewhere in B's constructor.
It doesn't have to be super(val); It can be super(42); or super(0) or super(val * 10);

The super(...) may be first line, last line, or somewhere in the middle of B's constructor.
B's constructor may do other stuff, but it HAS to include super(...).

D is a bit of a nanny in that it REQUIRES a derived class constructor to call super(val).
There is no way around it if you are using constructors.

If you can't abide by D's fussiness in this matter, there are other OOP languages such as C# and Eiffel that don't require a super(...) call in the derived constructor.

This is an example of D language INSISTING that a derived constructor call its base constructor. This is mostly to keep you out of trouble.

In this case, B's constructor discard A's constructor effects.

FWIW, it is not called a "shim". It is standard programming in D.

Here is working code:

import std.stdio;

        class A {
            int v;
            this(int val) {
                this.v = val;
            }
        }

        class B : A {
        	this(int val) {
        		super(0);          // Have to call super(...)
        		this.v = val * 10; // Force this.v to ignore whatever super(...) did.
        		writeln("In B's constructor, calling super(0), then going manual");
        	}
            int myfunc() {
                return this.v;
            }
        }

        void main() {
            auto b = new B(1);
            writeln(b.myfunc());
        }
     ```

A simpler way would be:

import std.stdio;

class A {
    int v;
    this(int val) {
        this.v = val;
    }
}

class B : A {
	this(int val) {
		super(val);
	}
    int myfunc() {
        return this.v;
    }
}

void main() {
    auto b = new B(1);
    writeln(b.myfunc());
}
July 24
On Thu, Jul 24, 2025 at 05:19:17PM +0000, Andy Valencia via Digitalmars-d-learn wrote:
> What is considered the "clean" way to address this error:
> 
> ```
> tst60.d(7): Error: class `tst60.B` cannot implicitly generate a
> default constructor when base class `tst60.A` is missing a default
> constructor
> ```

Your base class does not have a default constructor.  The only constructor it has takes a parameter `int val`.  This means derived classes need to invoke `super(...)` and provide an argument for `val`, since there's no way the compiler can divine what's the correct value to pass to the base class constructor if you don't specify what it should be.

If there is a default value that you wish to use when none are specified, either provide a default constructor that sets `val` to the default value, or specify a default value.


> Yes, this is a toy example, but it reflects something I'm doing in actual code.  The subclass has no state, thus it's fine to let the superclass take care of initialization.  Do I really need a shim like this:
> 
> ```d
>     this(int val) {
>         super(val);
>     }
> ```

In D, constructors are not inherited, so yes, unfortunately you have to write a forwarding ctor that passes the arguments along to the base class.  If you find this to be too much boilerplate, there are ways of using metaprogramming to automate it.  I believe code.dlang.org has a package or two that provides this, or you can write something yourself.

Here's an example of how to do it (caveat: I threw this together in ~5 mins, so there's probably lots of room for improvement):

```
import std;

/**
 * Automatically inject ctors that take the same arguments as base class ctors,
 * and forwards them to the respective base class ctor.
 */
mixin template ForwardBaseCtors() {
	static foreach (Base; BaseClassesTuple!(typeof(this))) {
		static foreach (func; MemberFunctionsTuple!(Base, "__ctor")) {
			this(Parameters!func args) {
				super(args);
			}
		}
	}
}

// Here's your base class
class B {
	// Here's a non-default ctor
	this(int val, string s, float f) { }
}

// Here's your derived class
class D : B {
	// Note: derived class ctor not explicitly specified here
	mixin ForwardBaseCtors;
}

void main() {
	// Voila!  The base class ctor got (semi-)automatically forwarded!
	B obj = new D(123, "abc", 3.14159);
}
```

Hope this helps.


T

-- 
What do you call someone who steals energy from the museum?  A Joule thief.
July 25
On Friday, 25 July 2025 at 02:40:35 UTC, H. S. Teoh wrote:

> In D, constructors are not inherited, so yes, unfortunately you have to write a forwarding ctor that passes the arguments along to the base class.

My OO worldview goes back to Smalltalk, where constructors are "just" methods and thus you can reason about them as you would any other inherited method.  I see the point of nudging explicit treatment of superclass constructors.  Is it nannying?  A bit.  Nothing that would make me walk away from D.  And I appreciate this extra insight into how D looks at constructors.

Thank you--both of you!
Andy

July 25
Another way to forward any constructor call is to do this:

import std.stdio;

class A {
  int x;
  this(int x) {
    this.x = x;
  }
}

class B : A {
  this(Args...)(Args args) {
    super(args);
  }
}

void main() {
  auto b = new B(42);
  b.x.writeln;
}
July 25
On Friday, 25 July 2025 at 03:57:34 UTC, Andy Valencia wrote:
> On Friday, 25 July 2025 at 02:40:35 UTC, H. S. Teoh wrote:
>
>> In D, constructors are not inherited, so yes, unfortunately you have to write a forwarding ctor that passes the arguments along to the base class.
>
> My OO worldview goes back to Smalltalk, where constructors are "just" methods and thus you can reason about them as you would any other inherited method.  I see the point of nudging explicit treatment of superclass constructors.  Is it nannying?  A bit.  Nothing that would make me walk away from D.  And I appreciate this extra insight into how D looks at constructors.
>
> Thank you--both of you!
> Andy

In Eiffel, constructors are also "just" methods, which are added to "create" section to be elevated to constructor status.
5 days ago
On Friday, 25 July 2025 at 03:57:34 UTC, Andy Valencia wrote:
> On Friday, 25 July 2025 at 02:40:35 UTC, H. S. Teoh wrote:
>
>> In D, constructors are not inherited, so yes, unfortunately you have to write a forwarding ctor that passes the arguments along to the base class.
>
> My OO worldview goes back to Smalltalk, where constructors are "just" methods and thus you can reason about them as you would any other inherited method.  I see the point of nudging explicit treatment of superclass constructors.  Is it nannying?  A bit.  Nothing that would make me walk away from D.  And I appreciate this extra insight into how D looks at constructors.
>
> Thank you--both of you!
> Andy

For a more technical explanation; in D constructors don’t live in the class vtable. So they can never be virtual. As such constructors are only OOP at compile time. This is why you need to call super constructors explicitly; otherwise you could end up corrupting state of objects you’ve inherited from.

Constructors do have entries in the Runtime Type Info (TypeInfo_Class) but those are seperate static instances from the overall class vtable (though vtable entry 0 points to the type info usually)

You can of course also break the type system if you really want to; but I’d recommend against doing so.