D 接口和模板功能

D 接口和模板功能,d,function-templates,D,Function Templates,我试图在两个不同的类上建立接口,其中函数的实现在子类中。它适用于常规函数,但不幸的是不适用于模板函数 见示例: import std.conv; import std.stdio; interface Num { T num(T)(); } class A : Num { T num(T)() { return 5.to!T; } } class B : Num { T num(T)() { return 2.to!T;

我试图在两个不同的类上建立接口,其中函数的实现在子类中。它适用于常规函数,但不幸的是不适用于模板函数

见示例:

import std.conv;
import std.stdio;

interface Num {
    T num(T)();
}

class A : Num {
    T num(T)() {
        return 5.to!T;
    }
}

class B : Num {
    T num(T)() {
        return 2.to!T;
    }
}

void main() {
    auto a = new A();
    auto b = new B();
    Num somea = a;
    Num someb = b;
    writeln(a.num!int());
    writeln(somea.num!int());
    writeln(someb.num!int());
    writeln(somea.num!string());
    writeln(someb.num!string());
}
(也可在线获取:)

结果是一个错误:

onlineapp.d:26: error: undefined reference to '_D9onlineapp3Num__T3numTiZQhMFZi'
onlineapp.d:27: error: undefined reference to '_D9onlineapp3Num__T3numTiZQhMFZi'
onlineapp.d:28: error: undefined reference to '_D9onlineapp3Num__T3numTAyaZQjMFZQj'
onlineapp.d:29: error: undefined reference to '_D9onlineapp3Num__T3numTAyaZQjMFZQj'
collect2: error: ld returned 1 exit status
Error: linker exited with status 1

我想要的东西可能实现吗?如果是,如何?

接口需要具体类型,以便编译器知道在虚拟函数表中为每个类保留多少插槽。它还需要足够的信息来可靠地判断接口是否实际实现

对于这样的转换,我只列出所需的特定类型<代码>静态foreach会有所帮助。考虑下面的代码:

import std.conv;
import std.stdio;
import std.meta : AliasSeq; // used for the static foreach list

interface Num {
    // this is the templated interface. You are allowed to have final
    // template members in an interface, with a body included.
    public final T num(T)() {
       T tmp;
       numImpl(tmp); // this forwards to the virtual function...
       return tmp;
    }

    // Here is the explicit list of types we want supported in the interface
    // it must be listed so the compiler knows how many vtable slots to assign
    protected alias NumImplTypes = AliasSeq!(int, string);
    // and now it declares the functions. To follow D overload rules, the
    // arguments for each must be different; we can't just rely on return
    // types. That's why I did it as a ref thing.
    static foreach(T; NumImplTypes)
            protected void numImpl(ref T t);
}

class A : Num {
    // and now, in each child class, we just do the foreach implementation, 
    // looking very similar to the template. But it isn't a template anymore
    // which allows it to be virtual.
    static foreach(T; NumImplTypes)
    protected void numImpl(ref T t) {
        t = 5.to!T;
    }
}

class B : Num {
    // ditto
    static foreach(T; NumImplTypes)
    protected void numImpl(ref T t) {
        t = 2.to!T;
    }
}

// this is the same as in your example
void main() {
    auto a = new A();
    auto b = new B();
    Num somea = a;
    Num someb = b;
    writeln(a.num!int());
    writeln(somea.num!int());
    writeln(someb.num!int());
    writeln(somea.num!string());
    writeln(someb.num!string());
}

经过昨晚的反复试验,我想出了另一个解决办法。它不包括AliasSeq,也不需要有明确的类型列表。但是,限制是您需要事先知道所有子类(我知道):


你好非常感谢你。这很有效。同时,我提出了另一个解决方案,它不需要我列出可能的类型,但有其他限制。我把它放在另一个答案里。
import std.conv;
import std.stdio;

interface Num {
    // this is what's changed
    T num(T)() {
        if(cast(A) this) { return (cast(A) this).num!T(); }
        if(cast(B) this) { return (cast(B) this).num!T(); }
        throw new Exception("Unknown subclass");
    }
}

class A : Num {
    T num(T)() {
        return 5.to!T;
    }
}

class B : Num {
    T num(T)() {
        return 2.to!T;
    }
}

void main() {
    auto a = new A();
    auto b = new B();
    Num somea = a;
    Num someb = b;
    writeln(a.num!int());
    writeln(somea.num!int());
    writeln(someb.num!int());
    writeln(somea.num!string());
    writeln(someb.num!string());
}