泛型类型的签名约束

时间:2012-07-07 20:42:17

标签: d

struct S(int a, int b) { }

void fun(T)(T t) { }

我希望fun仅与S合作。签名约束会是什么样的?

我无法让fun成为S的成员,而void fun(T)(T t) if(is(T : S)) { }我会Error: struct t1.S(int a,int b) is used as a type

3 个答案:

答案 0 :(得分:10)

S不是一种类型。它是一种类型的模板。 S!(5, 4)是一种类型。 S的不同实例化很可能会生成完全不同的代码,因此S!(5, 4)的定义可能完全S!(2, 5)不同。例如,S可能是

struct S(int a, int b)
{
    static if(a > 3)
        string foo;

    static if(b == 4)
        int boz = 17;
    else
        float boz = 2.1;
}

请注意,成员变量的数量和类型不同,因此您无法真正使用S!(5, 4)代替S!(2, 5)。它们也可能是名为UV的结构体,它们根本没有模板化它们彼此之间的所有关系。

现在,特定模板的不同实例化关于它们的API通常是相似的(或者它们可能不会使用相同的模板完成),但从编译器的角度来看,它们彼此之间没有关系。因此,处理它的常用方法是纯粹使用类型的API而不是其名称或实例化的模板。

因此,如果您希望S拥有foobarfoozle函数,并且您希望fun使用这些函数,那么你将构造一个约束,测试给fun的类型具有那些函数并且它们按预期工作。例如

void fun(T)(T t)
    if(is({ auto a = t.foo(); t.bar(a); int i = t.foozle("hello", 22);}))
{}

然后任何类型都有一个名为foo的函数返回一个值,一个名为bar的函数可能会返回一个值,也可能不会返回一个值foo的结果,以及一个名为foozle的函数接受stringint并返回int将使用fun进行编译。因此,fun比仅坚持使用S的实例化要灵活得多。在大多数情况下,这些约束被分离为单独的同名模板(例如isForwardRangeisDynamicArray),而不是将原始代码放在is expression中,以便它们可以重用(并且更加用户友好) ),但这样的表达式就是这种同名模板在内部使用的。

现在,如果确实坚持限制fun,使其仅适用于S的实例化,那么我知道有两个选项。< / p>

1。添加S始终拥有的某种声明,并且您不希望任何其他类型的声明。例如

struct S(int a, int b)
{
    enum isS = true;
}

void fun(T)(T t)
    if(is(typeof(T.isS)))
{}

请注意,声明的实际值无关紧要(也不是其类型)。这是一个简单的事实,它存在你正在测试。

2。更优雅(但不那么明显的解决方案)就是这样做:

struct S(int a, int b)
{
}

void fun(T)(T t)
    if(is(T u : S!(i, j), int i, int j))
{}
一旦变得非常复杂,

is expressions就会对伏都教有一定的倾向,但带有逗号的版本正是你所需要的。 T u是您正在测试的类型和标识符; : S!(i, j)给出了您希望T成为实例化的模板特化;其余的是TemplateParameterList,声明左边的东西中使用的符号,但之前没有声明过 - 在这种情况下,ij

答案 1 :(得分:7)

我认为在其他答案中有一些小红色鲱鱼。您可以使用模式匹配来确定T是否是S的某个实例,如下所示。

最简单的方法是对参数本身进行模式匹配:

void fun(int a, int b)(S!(a, b) t) {
}

更一般地说,你可以在模板约束中的分离中进行模式匹配:

void fun(T)(T t) if (is(T U == S!(a, b), int a, int b)) {
}

在这两种情况下,您都可以访问实例化参数。

答案 2 :(得分:3)

“只使用S在D中没有意义,因为 S不是类型,而是模板

与其他语言不同,D中的模板本身“某事物”。

你写的是简写

template S(int a, int b) { struct S { } }

因此类型的全名为S(a, b).S,适用于您使用的ab。没有办法让它“一般地”引用S

如果您需要设置这样的约束,我建议在S中放置一些私有内容,并检查T是否具有相同的成员。