问题描述
我试图在两个不同的类之间建立接口,而函数的实现位于子类中.它适用于常规功能,但不幸的是不适用于模板功能.
I'm trying to have interface over two different classes where implementation of a function is in the subclass. It works for regular functions, but unfortunately not for template functions.
查看示例:
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());
}
(也可在线获得: https://run.dlang.io/is/Nl1edV)
结果是错误:
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
我想实现什么?如果可以,怎么办?
Is what I want possible to achieve? If so, how?
推荐答案
接口需要具体的类型,因此编译器知道在虚拟函数表中为每个类保留多少个插槽.它还需要足够的信息来可靠地判断该接口是否真正实现.
Interfaces need concrete types so the compiler knows how many slots to reserve in the virtual function table for each class. It also needs enough information to reliably tell if the interface is actually implemented.
对于这样的转换,我只列出所需的特定类型. static foreach
可以帮助您.考虑以下代码:
For a case of conversion like this, I'd just list out the specific types needed. static foreach
can help. Consider the following code:
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());
}
这篇关于界面和模板功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!