Thread overview
Templates for instantiating derived class
Sep 20, 2021
rjkilpatrick
Sep 20, 2021
Adam Ruppe
September 20, 2021

Essentially, I would like to write a template that calls the constructor of the parent class or the constructor of the inherited class, depending on its type.

#!/usr/bin/env dub
/+ dub.sdl:
	name "mwe"
+/
class Super {
    private int _a;
    this(){}
    this(int a) {
        _a = a;
    }

    auto opBinary(string op)(int rhs) const if (op == "+") {
        return new Super(_a + rhs); // Creates of type Super even when called from derived class
    }
}

class Derived : Super {
    this(){}
    this(int a) {
        _a = a + 1;
    }
}

void main() {
    import std : writeln;

    Super foo = new Super(1);
    Super foo2 = foo + 1; // Works fine as calls `Super` constructor

    Derived bar = new Derived(2);
    Derived bar2 = bar + 1; // Cannot implicitly cast `Super` to `Derived`
}

Some kind of return new this(...) would be good, but that's not possible.
I think it has to be done with templates, but I'm not sure how to do this.

Any help would be greatly appreciated.

September 20, 2021
On Monday, 20 September 2021 at 22:16:47 UTC, rjkilpatrick wrote:
>     auto opBinary(string op)(int rhs) const if (op == "+") {
>         return new Super(_a + rhs); // Creates of type Super even when called from derived class
>     }

Make this

auto opBinary(string op, this This)(int rhs) .............
          return new This(_a + rhs);
}


The template this param is the static type it is called on.

https://dlang.org/spec/template.html#template_this_parameter


Note though that this is the static type. If you  do

Super thing = new Derived();
thing + 5;


it will still return Super since that's the type it was called through. If you want it to actually return derived, you'll have to add a virtual factory function:


class Super {
    protected Super factory() { return new Super(); }
}

class Derived : Super {
    override Derived factory() { return new Derived(); }
}


Then you can call obj.factory to get the right dynamic type. (forward args as needed etc.)

> Some kind of `return new this(...)` would be good, but that's not possible.

You can do `return new typeof(this)(...);` fyi but it is again the static type of this, which will be whatever it is where it is defined. This is a useful trick if you were to make that factory function a mixin template or something though.
September 20, 2021

On 9/20/21 6:16 PM, rjkilpatrick wrote:

>

Essentially, I would like to write a template that calls the constructor of the parent class or the constructor of the inherited class, depending on its type.

...

>

Some kind of return new this(...) would be good, but that's not possible.
I think it has to be done with templates, but I'm not sure how to do this.

Any help would be greatly appreciated.

What you want is to change that operator into a virtual function. Yes, you still have to write the overrides, but you could if you want use a mixin. Adam's solution works, but only uses the static type.

class Super {
    private int _a;
    this(){}
    this(int a) {
        _a = a;
    }

    Super performAdd(int rhs) const {
        return new Super(_a + rhs);
    }

    alias opBinary(string op : "+") = performAdd;
}

class Derived : Super {
    this(){}
    this(int a) {
        _a = a + 1;
    }

    override Derived performAdd(int rhs) {
        return new Derived(_a + rhs);
    }
}

void main() {
    import std : writeln;

    Super foo = new Super(1);
    Super foo2 = foo + 1; // Works fine as calls `Super` constructor

    Derived bar = new Derived(2);
    Derived bar2 = bar + 1; // works now

    Super b2 = bar;
    Derived d2 = cast(Derived)(b2 + 1); // ok, *and* calls Derive's version of performAdd
    assert(d2 !is null && d2._a == bar2._a);
}