模板引用父类中的子类

时间:2017-10-19 11:19:20

标签: oop d

在D中,是否可以从父类中引用子(继承)类?

我试过这样的事情:

abstract public @property typeof(this)[] sequence();

希望typeof(this)将解析为覆盖该方法而不是父类的子类;但事实并非如此。有没有办法做到这一点?

提前致谢。

1 个答案:

答案 0 :(得分:3)

听起来像你在寻找template this parameters

class Base { T[] sequence(this T)() { return null; } }
class Derived : Base {}
static assert(is(typeof((new Derived).sequence()) == Derived[]));

但是,请注意,上面的sequence不是虚函数,而且模板也是在基类的上下文中实例化的。要为用户提供简单的界面,您可能希望转发到专门的功能。这可以是一个虚函数(在这种情况下你需要转换函数的返回值),或者是子类中的duck-typed函数:

class Base {
    T[] sequence(this T)() {
        // Virtual call, requires unsafe cast of return type.
        return cast(T[])sequenceImplVirtual();
        // Non-virtual call, requires safe cast of this reference
        // and will fail if the subclass doesn't implement it correctly.
        return (cast(T)this).sequenceImplDuck();
    }
    abstract Base[] sequenceImplVirtual();
}

class Derived : Base {
    Derived[] sequenceImplDuck() {
        return [this];
    }
    override Base[] sequenceImplVirtual() {
        return [this];
    }
}

unittest {
    Derived[] arr = (new Derived).sequence;
}

虚拟调用可能看起来最具吸引力,因为如果子类无法实现sequenceImplVirtual,则会产生编译错误。但是请注意,重写功能并不声称会返回Derived[],如果您错误地返回了Base或其他不是从Derived派生的类该程序将是段错误的。明确的演员有效地隐藏了这一点。一个稍微冗长的程序可以测试这个:

T[] sequence(this T)() {
    import std.algorithm.searching : all;
    auto result = sequenceImplVirtual();
    assert(result.all!((Base a) => a is null || cast(T)a !is null));
    return cast(T[])result;
}

如果sequenceImplVirtual返回无效值,这将在运行时提供易于理解的断言错误。

另一方面,鸭式解决方案并未表明您在使用之前忘记实施sequenceImplDuck。但是,由于它只执行安全转换(cast(T)this),编译器保证返回值确实是Derived[]

class Base {
    T[] sequence(this T)() {
        return (cast(T)this).sequenceImplDuck();
    }
}

class Derived : Base {
    // Note: wrong return type.
    // Will fail to compile when you call sequence().
    Base[] sequenceImplDuck() {
        return [this];
    }
}

class Derived2 : Base {
    // Note: No implementation.
    // Will fail to compile when you call sequence().
}

unittest {
    Derived[] arr = (new Derived).sequence;
    auto d = new Derived2;
    auto arr2 = d.sequence;
}

使用-unittest编译时,上述操作会失败,但如果您注释掉unittest,或者编译时没有-unittest,编译器将不会指示Derived或{{1}没有正确实现所需的功能,虚拟呼叫将会。