在 D(LDC2) 中调用模板函数时,委托被推导为无效

delegate is deduced to void when calling template function in D(LDC2)

我有一个使用 Algebraic.

编写的自定义 Option 类型
struct None{};
struct Some(T){
    T t;

    alias t this;

    T get(){
        return t;
    }
};

alias Option(T) = Algebraic!(Some!(T), None);
Option!T some(T)(T t){
    return Option!T(Some!T(t));
}
Option!T none(T)(){
    return Option!T(None());
}

然后我尝试写一些基本的便利函数:

T getValue(T,uint size)(VariantN!(size, Some!T, None) opt){
    return opt.get!(Some!T).t;
}

bool isDefined(T, uint size)(VariantN!(size, Some!T, None) opt){
    return opt.convertsTo!(Some!T);
}

A match(A,T,uint size)(VariantN!(size, Some!T, None) opt, A delegate(T) some, A delegate() none){
    if(opt.isDefined!(T,size)){
        return some(opt.getValue!(T,size));
    }else{
        return none();
    }
}

当调用 match 编译器无法推断出模板的正确参数时:

 Option!int test = some(1);
 bool boolReturn = test.match((x) => false, () => true);

错误:

    Error: template util.match cannot deduce function from argument types !()(VariantN!(4LU, Some!int, None), void, bool function() pure nothrow @nogc @safe), candidates are:
src/util.d(79,3):        util.match(A, T, uint size)(VariantN!(size, Some!T, None) opt, A delegate(T) some, A delegate() none)

错误输出表明 match 的第二个参数(意思是 bool delegate(int)(x) => false)被推断为 void。为什么?

此示例编译(完全相同,但明确给出了 x 的类型):

Option!int test = some(1);
bool boolReturn = test.match((int x) => false, () => true);

如果委托中没有给出类型名称,它会将其作为模板(在错误消息中键入 void),在实例化时将具有推断的类型......在这里它想被推断为类型T,本身就是根据参数推断出的参数。

问题是编译器试图推断 (x) => 模板,同时推断函数调用,但不知道先做哪一个,所以它不能深入足够。如果你在任何一个地方明确提到它,它就会打破循环:

//有效 bool boolReturn = test.match!(bool, int)((x) => false, () => true);

//有效 test.match((int x) => false)

但我不确定如何自动执行...我尝试用不同的安排将它们解耦,但还没有成功....

Phobos 通常解决这个问题的方法是将委托作为 alias 模板参数而不是运行时参数。将签名更改为:

typeof(none()) match(alias some, alias none, T, uint size)(VariantN!(size, Some!T, None) opt) {

并将调用更改为:

 bool boolReturn = test.match!((x) => false, () => true);

之所以能够编译,是因为您将推理移到了两层:首先,编译器接受那些仍处于模板形式的委托作为参数。然后它接受 test 并计算出它的类型来推断其他参数。然后它进入正文并实际看到 调用 some,并在正文中实例化它的参数类型。之所以有效,是因为推断类型是在主体外部的签名层已知类型 T 之后完成的。

但是如果不使用模板函数...我不知道,我认为解决方案是做两层,以便显式类型在一个或另一个中命名,但我只是没有'我还没有想出来(而且 tbh 可能会停止尝试,因为我现在还有其他事情要做)。