www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Templates for instantiating derived class

reply rjkilpatrick <nothanks example.com> writes:
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.

```d

/+ 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.
Sep 20 2021
next sibling parent Adam Ruppe <destructionator gmail.com> writes:
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.
Sep 20 2021
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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. ```d 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); } ```
Sep 20 2021